function fun(){
const [contentArray,setContentArray] = useState([]);
setContentArray([...contentArray, <A />]);
setContentArray([...contentArray, <A />]);
setContentArray([...contentArray, <A />]);
return(
<div>
{
contentArray.map( (component:React.ReactNode,i:number) => {
return(
component
)}
}
</div>
})
);
}
above code works fine as long as i dont want to pass any prop to these array elements of "contentArray" array.but i want to pass indexes as props to these array elements
code sandbox: https://codesandbox.io/s/quirky-cray-u4jr1?from-embed
while setting the state it should be done as below
setState(prevState => [...prevState, A]
and while rendering it it should b rendered as below
{state.map((Comp, i) => {
return <Comp key={i} index={i} />;
})}
Note: no need to specify type as React.ReactNode, and you need to lookout for not setting state inside render, react doesn't allow it, in order to avoid infinite loop.
Thanks for showing way to do it #bpas247
https://codesandbox.io/s/broken-flower-z87mt
Related
Let's say that I have this component:
const Test = ({ children, ...rest }) => {
return <>{children}</>
};
export default Test;
I am wondering if it is possible to create a variable that holds the component like this:
const test = <Test></Test>;
And then loop over some data and push children to the test variable on every iteration.
if you don't have the data yet, then all you have to do is conditionally render your component when you do have the data.
{ data ? (<Test>{data.map(...)}</Test>) : <SomeOtherComponent /> /* or null */}
or
{ data ? <>{data.map((x) => <Test>{x}</Test>)}</> : <SomeOtherComponent /> /* or null */}
depending on what you want achieve, i didn't fully understand your question
i.e. if you have the data you need, render the component, rendering the children as you see fit, otherwise render some other component (or null, to render nothing)
Yeap, try that pattern:
const test = (children) => <Test>{children}</Test>;
and usage
<>
{[1,2,3].map(el=>test(el))}
</>
[Edited]
const TestComp = ({children}) => <Test>{children}</Test>;
<>
{[1,2,3].map(el=>(<TestComp>{el}</TestComp>))}
</>
so what I am trying to achieve here is storing a whole component in an array in a parent component which renders a specific component in the array using its index for example :
export const Test = () => {
const [components, setComponents] = useState([
<Order key={1} />,
<Order key={2} />,
<Order key={3} />,
]);
const [index, setIndex] = useState(0);
return (
<div>
<button onClick={() => setIndex((old) => (old + 1) % components.length)}>
change
</button>
{`page ` + index}
{components[index]}
</div>
);
};
const Order = () => {
const [someState, setSomeState] = useState(1);
return (
<div>
<button onClick={() => setSomeState((old) => old + 1)}>
{someState}
</button>
</div>
);
};
when I change the state of one item then cycle through the items then return to the item which I changed its state i found that it is not updated
what I figured out is that the component in the array (in the Test component) doesn't get updated and I couldn't figure out how to update it
what I don't want to do is storing the state of the order item in the parent and pass it as props (because it will be a pain to make it work)
const App = ({ flag }) => {
if (flag) <Order />
return null
}
I'm giving you an example so i can explain what might happen in your case. If the flag becomes false from a true, the App turns blank. But what happen to the Order? It's unmounted, why? Since when React compares between the previous scene and the current scene, it notice there's no such Order any more. So what you think about the memory of component of Order (which is called a fiber)?
I guess the answer is, the memory goes to be deleted and will be collected for future use.
Now back to your case, you are using an id to switch to different component. But in theory it should behave very similar to my example for each component.
NOTE: the take away is that if you want to use an array, that's fine, but all components has to be rendered at ALL time, you can hide them, but you can't unmount any of them.
what I don't want to do is storing the state of the order item in the
parent and pass it as props (because it will be a pain to make it
work)
Your problem is that when you render a Test component and then increase index, then you render another Test component with a different key, so reacts reconciliation algorithm unmounts the old one and you lose the state.
You have two options:
lift state of each Test component up, then when one gets unmounted, you will remount it with the old state, because state will be stored in parent, it will not be lost
another option is to render all components and only show those which you want using CSS display property, this way none of them gets unmounted and you retain state. Here is example:
const Order = () => {
const [someState, setSomeState] = React.useState(1);
return (
<div>
<button onClick={() => setSomeState((old) => old + 1)}>
{someState}
</button>
</div>
);
};
let components = [<Order />, <Order />, <Order />];
const Test = () => {
const [index, setIndex] = React.useState(0);
return (
<div>
<button onClick={() => setIndex((old) => (old + 1) % components.length)}>
change
</button>
{`page ` + index}
{[0, 1, 2].map((x) => (
<div key={x} style={{ display: index === x ? "block" : "none" }}>
{components[x]}
</div>
))}
</div>
);
};
ReactDOM.render(
<Test />,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="react"></div>
PS I have removed components from state, I can't find official info now, but IMHO it is not good idea to store components in state.
I have an array. in which i want to iterate over and do conditional checks and display the correct element. i'm using functional component.
Below is an example :
import React, { useState, useEffect } from "react";
function Job(props) {
const [resp_data, setdata] = useState("");
const [look, setlook] = useState("");
useEffect(() => {
// some api calls happen here and response data is stored in state resp_data
}, [props]);
return (
<>
<div className="product_list">
{resp_data.length > 0
? resp_data.map((item) => {
{item.looking_for === "work" ? (
<div className="card-rows work" key={item.id}>
work
</div>
) : (<div className="card-rows no_work" key={item.id}>
No work
</div>)
}
})
: <div>array length is 0</div>}
</div>
</>
);
}
export default Job;
The response data received from api call happening inside useEffect is stored in state variable resp_data.
if resp_data length is not zero, i need to iterate over the list and check if the field value "looking_for" is equal to "work"
if true display a component , else display another component.
if resp_data length is zero display "array length is zero"
this i want to implement, but i could not find many answers for functional components. i have tried many possible ways by switching, changing the braces etc..my hard luck.
Any help is greatly appreciated.
You are not returning anything from the 'map' function.
resp_data.map((item) => (
item.looking_for === "work" ? (
<div className="card-rows work" key={item.id}>
work
</div>
) : (<div className="card-rows no_work" key={item.id}>
No work
</div>)
)
)
Let's say I have an app which looks like this:
<>
<Component />
<button>Add New Component</button>
</>
How can I make it so every time the button is clicked, a new <Component /> is being appended? It's not about conditional rendering when we show a component or hide it, It's about a possibility to add unlimited amount of new components. Do you have any ideas?
The general workflow is that you store component data (or just identifiers) in an array in state. You then map over the array to render your Component list. The button adds a new identifier/data set to the array.
const App = () => {
const [list, setList] = useState([0]);
const addComponent = () => {
setList([...list, list.length]);
};
return (
<>
{list.map(id => <Component key={id} />)}
<button onClick={addComponent}>Add New Component</button>
</>
)
};
This is a very simple example. In reality you would want to assign unique ids for the keys and probably package it with some more data as an object, but you get the idea.
i'm creating a simple react to do list, I'm currently working on a delete button, I have created an array then passed this array into a prop, I then need to splice that item from the prop array when the user clicks the delete button. I was able to store the array number but I cant seem to update the array after its deleted.
CLASS CALL:
<TodoList items={this.state.items} deleteItems={this.deleteItem}/>
SUB-CLASS CODE:
class TodoList extends Component {
constructor(props) {
super(props);
this.removeItem = this.removeItem.bind(this);
}
render() {
return (
<div>
{ this.props.items.map((item, i) => (
<div className={"col-12"} key={item.id}>
<div className={"card text-white"}>
<div className={item.priority}>
<div className={"col-12 card-body"}>
<h1>{item.title}</h1>
<p>{item.text}</p>
<button onClick={() => { this.removeItem(item, i)}} key={i} className={"col-12 btn btn-primary bg-red"}>Delete</button>
</div>
<div/>
</div>
</div>
</div>
))}
</div>
);
}
removeItem(e, i) {
this.props.items.splice(i, '');
console.log(i);
}
}
I have been looking at different stack questions but none of the solutions seem to apply to this, thanks for any constructive feedback :)
I believe <TodoList /> component should have its own state. However, if you can't do so, there's 2 solutions to this problem:
Keep <ToDoList /> component's state and props in sync (In case the parent component modifies the state passed down as items). Then modify the <TodoList /> 's state.
Declare a method that removes the item inside the parent component which has the
state, and pass it down as props (Recommended)
Example code:
class ParentComponent extends Component {
state = {
items: [1, 2, 3]
}
removeItem = index => () => {
this.setState(prevState => ({
items: prevState.items.filter((_, i) => i !== index) //Filter the items
}));
};
render() {
return (
<TodoList items={this.state.items} deleteItems={this.removeItem} />
);
}
}
Important: Always use pure functions to modify the state. Do not use .splice() or .push() (If you haven't cloned the state yet). It's always safer to use .filter(), .map(), .concat(), etc.