what I try to do is to have the same display as this picture :
So in my menu the plant type (Type of plant1) is displayed above a gray bar and when you click on the down chevron then you can see all the plants name, related to this type, with checkboxes on left, by default there will be all checked. And the blue rectangle indicates the number of plants that have been selected.
How can I do that, which package can help me in REACT?
Here my plants.json :
{
"plants_type": [
{
"_id_type": "1",
"name_type": "Type of plant1",
"plants": [
{
"name": "Plant1.1",
"_id": "2"
},
{
"name": "Plant1.2",
"_id": "3"
}
]
},
{
"_id_type": "4",
"name_type": "Type of plant2",
"plants": [
{
"name": "Plant2.1",
"_id": "5"
},
{
"name": "Plant2.2",
"_id": "6"
}
]
}
]
}
You can create a dropdown list on your own like below. I have added the logic of selecting items to the data itself.
You can keep a component called Category to keep a single state of the parent menu item. Whether it's open or not. Then iterate over the plants as checkbox inputs to make them selectable.
I have used a simple initialize function to make all the items selected initially. This should work as you expect. Add a console log of selectionMenu to see how selected property changes while toggling items.
Move the inline styles to CSS classes to make the code more clear.
const data = { plants_type: [ { _id_type: "1", name_type: "Type of plant1", plants: [ { name: "Plant1.1", _id: "2" }, { name: "Plant1.2", _id: "3" } ] }, { _id_type: "4", name_type: "Type of plant2", plants: [ { name: "Plant2.1", _id: "5" }, { name: "Plant2.2", _id: "6" } ] } ] };
const Category = ({ _id_type, name_type, plants, changeSelection }) => {
const [toggleState, setToggleState] = React.useState(false);
return (
<div key={_id_type}>
<div
style={{
cursor: "pointer",
userSelect: "none",
display: "flex",
margin: "2px",
backgroundColor: "lightgray"
}}
onClick={() => setToggleState((prev) => !prev)}
>
<div>{name_type}</div>
<div
style={{
backgroundColor: "blue",
color: "white",
padding: "0px 10px",
marginLeft: "auto"
}}
>
{plants.filter(({ selected }) => selected).length}
</div>
</div>
<div style={{ marginLeft: "10px" }}>
{toggleState &&
plants.map(({ name, _id, selected }) => (
<div key={_id}>
<input
key={_id}
type="checkbox"
value={name}
checked={selected}
onChange={(e) => changeSelection(_id_type, _id, e.target.value)}
/>
{name}
</div>
))}
</div>
</div>
);
};
const App = () => {
const initializeSelectionMenu = (data) => {
return data.map((item) => {
return {
...item,
plants: item.plants.map((plant) => ({ ...plant, selected: true }))
};
});
};
const [selectionMenu, setSelectionMenu] = React.useState(
initializeSelectionMenu(data.plants_type)
);
console.log(selectionMenu);
const changeSelection = (catId, itemId, value) => {
setSelectionMenu((prevSelectionMenu) =>
prevSelectionMenu.map((item) => {
if (item._id_type === catId) {
return {
...item,
plants: item.plants.map((plant) => {
if (plant._id === itemId) {
return { ...plant, selected: !plant.selected };
}
return plant;
})
};
}
return item;
})
);
};
return (
<div>
{selectionMenu.map((item) => (
<Category
{...item}
changeSelection={changeSelection}
key={item._id_type}
/>
))}
</div>
);
}
ReactDOM.render(<App />, document.querySelector('.react'));
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div class='react'></div>
Related
I'm receiving data from api like this:
{
"isTrue": true,
"data1": {
"username": "user1",
"images": [
"image1.jpg",
"image2.jpg"
]
},
"data2": {
"location": "new york",
"age": "80"
}
}
and setting it in my state as details.
I want to display data in flatlist, so I do this:
<FlatList
data={state.details}
renderItem={({item}) => {
<Details item={item} />;
}}
></FlatList>
in my component I render data like:
export default Details = ({item}) => {
return item
? Object.values(item).map(value => {
<Text>{value.data1?.username}</Text>;
})
: null;
};
Why don't the items render?
Make sure u pass Array to flat list
like :
const DATA = [
{
id: "bd7acbea-c1b1-46c2-aed5-3ad53abb28ba",
title: "First Item",
},
{
id: "3ac68afc-c605-48d3-a4f8-fbd91aa97f63",
title: "Second Item",
},
{
id: "58694a0f-3da1-471f-bd96-145571e29d72",
title: "Third Item",
},
];
flat list will do for loop for you, so u don't need to values(item).map you can do:
export default Details = ({item}) => {
return
<View>
<Text>{item.isTrue}</Text>
<Text>{item.data1.username}</Text>
</View>
};
change
renderItem={({item}) => {
<Details item={item} />;
}}
to
renderItem={({item}) => {
return <Details item={item} />;
}}
or
renderItem={({item}) => (<Details item={item} />)}
finally don't forget to add <View></View> as parent for <Text><Text>
I'm having issues with deleting elements in react-flow via a button.
I can delete elements fine using Backspace but the button only works for the first delete and after that it brings back the deleted node.
New to using react-flow and can't put my finger on the problem here. Is the state not getting changed some how?
Below is the code I use for react flow
CodeSandbox here.
import React, { useState, useCallback, useEffect } from "react";
import ReactFlow, {
removeElements,
addEdge,
Background,
} from "react-flow-renderer";
const onLoad = (reactFlowInstance) => {
reactFlowInstance.fitView();
console.log(reactFlowInstance.getElements());
};
const StackFlow = () => {
const initialElements = [
{
id: "0",
position: { x: 0, y: -100 },
sourcePosition: "bottom",
style: {
width: 100,
fontSize: 11,
color: "white",
background: "#6ec9c0",
},
data: {
label: (
<>
<button
className="w-md h-md border-2 border-black p-2"
onClick={() =>
remModelData("0")
}
>
Del
</button> <br /> <br />
<strong>Models</strong>
</>
),
},
},
{
id: "1",
position: { x: 100, y: 50 },
sourcePosition: "bottom",
targetPosition: "top",
data: {
label: (
<>
<button
className="w-md h-md border-2 border-black p-2"
onClick={() =>
remModelData("1")
}
>
Del
</button> <br /> <br />
Model: <strong>1</strong> <br />
ID: 1
</>
),
},
},
{
id: "2",
position: { x: 150, y: 250 },
sourcePosition: "bottom",
targetPosition: "top",
data: {
label: (
<>
<button
className="w-md h-md border-2 border-black p-2"
onClick={() =>
remModelData("2")
}
>
Del
</button> <br /> <br />
Model 1: <strong>subModel1</strong> <br />
ID: 2
</>
),
},
},
{
id: "3",
position: { x: 250, y: 250 },
sourcePosition: "bottom",
targetPosition: "top",
data: {
label: (
<>
<button
className="w-md h-md border-2 border-black p-2"
onClick={() =>
remModelData("3")
}
>
Del
</button> <br /> <br />
Model 1: <strong>subModel2</strong> <br />
ID: 3
</>
),
},
},
{
id: "0-1",
type: "step",
source: "0",
target: "1"
},
{
id: "1-2",
type: "step",
source: "1",
target: "2"
},
{
id: "1-3",
type: "step",
source: "1",
target: "3"
},
];
const [elements, setElements] = useState(initialElements);
const onElementsRemove = useCallback(
(elementsToRemove) =>
setElements((els) => removeElements(elementsToRemove, els)),
[]
);
const onConnect = (params) => setElements((els) => addEdge(params, els));
const [reactflowInstance, setReactflowInstance] = useState(null);
useEffect(() => {
if (reactflowInstance && elements.length > 0) {
reactflowInstance.fitView();
}
}, [reactflowInstance, elements.length]);
const remModelData = useCallback((id) => {
let arr = elements
var index = arr.indexOf(id);
if (index > -1) {
arr.splice(index, 1);
}
arr = arr.filter(function (obj) {
return obj.id !== id;
});
console.log(arr);
setElements(arr);
}, []);
return (
<ReactFlow
elements={elements}
onElementsRemove={onElementsRemove}
onConnect={onConnect}
onLoad={onLoad}
snapToGrid={true}
snapGrid={[15, 15]}
>
<Background color="#aaa" gap={16} />
</ReactFlow>
);
};
function Home() {
return (
<>
<div className="w-full mx-auto justify-center items-center flex">
<div
className="flex mt-10 flex-row items-center content-center justify-center max-h-5xl max-w-5xl py-2"
style={{ flex: 1, width: 1000, height: 800, borderWidth: 2 }}
>
<StackFlow />
</div>
</div>
</>
);
}
export default Home;
I was able to accomplish the end result I wanted by creating this function:
const deleteNode = (id) => {
setElements((els) => removeElements([elements[id]], els));
};
And passing this into the OnClick:
onClick={() => deleteNode(0)}
Changing 0 for what ever index the element you wish to remove from the array is
As from version 10 Elements has been replaced with Nodes and Edges.
I could achieve the following using the function below.
const deleteNodeById = (id) => {
flowInstance.setNodes((nds) => nds.filter((node) => node.id !== id))
}
And passing this function to my delete button (In my case mui delete icon)
<DeleteOutlined
onClick={() => deleteNodeById(data.id)}
style={{ color: '#FF0000' }}
/>
As of version 11.2 onwards, you can use
reactFlowInstance.deleteElements({ nodes: nodesToDelete, edges: edgesToDelete });
The advantage is that method will fire "onNodesDelete" & "onEdgesDelete" events so that you can reuse your handlers.
Recall that these events are also fired if you press the "backspace" button.
I'm attempting to create a quantity filter based on color, size, or both. When I click the red color, for example, it displays the whole quantity of the red color, but if I press color red and size small, it displays the exact quantity I require. Is there a way to accomplish this?
This is what I mean.
When I select a color or a size, the quantity should be displayed. Also, there should not be a duplication of my error, since there are three red colors listed above the image.
Code
import React, { useState } from "react";
export default function ControlledRadios() {
const [qty, setQty] = useState(0);
const data = [
{
id: 1,
name: "Product A",
attributes: [
{
id: 1,
color: "Red",
size: "Small",
qty: 200,
},
{
id: 2,
color: "Red",
size: "Medium",
qty: 100,
},
{
id: 3,
color: "Red",
size: "Large",
qty: 300,
},
{
id: 4,
color: "Yellow",
size: "Small",
qty: 200,
},
{
id: 5,
color: "Yellow",
size: "Medium",
qty: 100,
},
{
id: 6,
color: "Yellow",
size: "Large",
qty: 300,
},
],
},
];
const handleChange = (event) => {
setQty(event.target.value);
};
return (
<>
<h1>Quantity: {qty}</h1>
<fieldset value={qty} onChange={(event) => handleChange(event)}>
<h3>Color:</h3>
{data?.map(({ attributes }) => {
return attributes.map(({ id, ...rest }) => (
<>
<label key={id}>
<input
type="radio"
name="schedule-weekly-option"
value={rest.qty}
/>
{rest.color}
</label>
<br />
</>
));
})}
<h3>Size:</h3>
{data?.map(({ attributes }) => {
return attributes.map(({ id, ...rest }) => (
<>
<label key={id}>
<input
type="radio"
name="schedule-weekly-option"
value={rest.qty}
/>
{rest.size}
</label>
<br />
</>
));
})}
</fieldset>
</>
);
}
As seen in the data value, I have multiple color and sizes goes something like this for example {color: "Red", Size: "Small", qty: 200} I have multiple red values, so right every red should be added so when I click the Red radio button it should display the quantity 600 because the quantity of all the red will be added. but if I press like color Red and size Small it should display 200.
PS: If possible can u not make duplicate of colors like I did 3 color red and yellow, to make it only 1 Red and 1 Yellow same goes with the size.
If you need anymore clarification you need or explanation please comment down below. Thanks
Phew! This should do the trick. The idea is to pass more data to your handleChange handleChange - see how I changed it? I grab the data straight out of the target element, which is not ideal, but works.
I didn't do the filtering for you, good luck with that :) Should be just adding a (granted, a fairly complex) .filter(...) to your .map(...).
document.onreadystatechange = () => {
const {useState} = React;
function ControlledRadios() {
const [qty, setQty] = useState(0);
const data = [
{
id: 1,
name: "Product A",
attributes: [
{
id: 1,
color: "Red",
size: "Small",
qty: 200,
},
{
id: 2,
color: "Red",
size: "Medium",
qty: 100,
},
{
id: 3,
color: "Red",
size: "Large",
qty: 300,
},
{
id: 4,
color: "Yellow",
size: "Small",
qty: 200,
},
{
id: 5,
color: "Yellow",
size: "Medium",
qty: 100,
},
{
id: 6,
color: "Yellow",
size: "Large",
qty: 999,
},
],
},
];
const handleChange = (event) => {
const id = event.target.value;
const targetAttribute = data[0].attributes.find(x => x.id == id);
if (event.target.name === "schedule-weekly-option-color") {
let sum = 0;
data[0].attributes.forEach((a) => {
if (a.color===targetAttribute.color) {
sum += a.qty;
}
});
setQty(sum);
} else {
let sum = 0;
data[0].attributes.forEach((a) => {
if (a.color===targetAttribute.color && a.size===targetAttribute.size) {
sum += a.qty;
}
});
setQty(sum);
}
};
return (
<React.Fragment>
<h1>Quantity: {qty}</h1>
<fieldset value={qty} onChange={(event) => handleChange(event)}>
<h3>Color:</h3>
{data.map(({ attributes }) => {
return attributes.map(a => (
<label key={a.id}>
<input
type="radio"
name="schedule-weekly-option-color"
value={a.id}
/>
{a.color}
</label>
));
})}
<h3>Size:</h3>
{data.map(item => {
return item.attributes.map(a => (
<label key={a.id}>
<input
type="radio"
name="schedule-weekly-option-size"
value={a.id}
/>
{a.size}
</label>
));
})}
</fieldset>
</React.Fragment>
);
}
ReactDOM.render(<ControlledRadios />, document.body);
};
<script crossorigin src="https://unpkg.com/react#17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#17/umd/react-dom.development.js"></script>
I want to update state of key heart in the array's objects when the heart icon pressed it changes to red so for this I'm using react native icons and i'm using heart and hearto to switch when click on it
here is the code:
state = {
localAdversiment: [
{
title: "Ecloninear 871",
image: require("../../assets/images/truck_image.png"),
year: "2015",
type: "Truck",
status: "new",
price: "$ 2000",
heart: "hearto"
}
Here it function which is called when heart icon pressed
handleFavourite = index => {
const { heart } = this.state.localAdversiment[index];
this.setState(
{
heart: "heart"
}
);
};
here is the heart icon code
<TouchableOpacity onPress={() => this.handleFavourite(index)}>
<Icon
name={item.heart}
type={"AntDesign"}
style={{ fontSize: 18 }}
/>
</TouchableOpacity>
kindly help me how to update heart as heart instead of hearto when clicked
You can do it easily by following approach
state = {
localAdversiment: [
{
id: 0,
title: "Ecloninear 871",
image: require("../../assets/images/truck_image.png"),
year: "2015",
type: "Truck",
status: "new",
price: "$ 2000",
heart: "hearto",
selected: false
}
}
now in onPress do this
handleFavourite = (item) => {
const { id } = item;
this.setState({
localAdvertisement: this.state.localAdvertisement.map((item) => {
if(item.id === id){
return {
...item,
selected: !item.selected
}
}
return item
})
})
};
Now render like this
<TouchableOpacity onPress={() => this.handleFavourite(item)}>
<Icon
name={item.selected ? "heart" : 'hearto'}
type={"AntDesign"}
style={{ fontSize: 18 }}
/>
</TouchableOpacity>
Hope it will help you
Edit this function as follows:
handleFavourite = index => {
let updatedlocalAdversimentStates = this.state.localAdversiment;
updatedlocalAdversimentStates[index].heart = "heart";
this.setState(
{
localAdversiment: updatedlocalAdversimentStates
}
);
};
I'm using map to iterate throug a list of elements, what I want is to have 4 elements per row. This is my render part of the class:
render() {
return (
<div>
<h1>Card List</h1>
{this.props.items ? this.props.items.map((item) =>
<div class="cardSection">
<Row className="show-grid">
<Card name={item.name} imgGold={item.imgGold} />
</Row>
</div>
) : <div>Loading!</div>}
</div>
)
}
}
I want 4 Card components per row using xs={4} md={4}
You can do something like
const items = [
{ name: "1" },
{ name: "2" },
{ name: "3" },
{ name: "4" },
{ name: "5" },
{ name: "6" },
{ name: "7" },
{ name: "8" },
{ name: "9" },
{ name: "10" },
{ name: "11" },
];
const App = () => {
const result = items.map((x,i) => {
return i % 4 === 0 ? items.slice(i, i+4) : null;
}).filter(x => x != null);
return (
<div>
{result.map((result, index) => {
return (<section key={index}>
{result.map(item => <span>{item.name}</span>)}
</section>);
})}
</div>
);
};
ReactDOM.render(<App />, document.getElementById("root"));
span {
margin-left: 5px;
margin-right: 5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
This will group your item array into groups of 4. You can even make it accept any number you want.
You can then iterate and render each of them in groups of 4.
Please make sure you run the snippet so you can see it working.