Components A, B and C are functional components. If someState is true and component A is visited, will the useState variables retain their values when arriving back at this module?
The return from A is handled in this main module by passing a callback function to A which resets someState.
const [someState, setSomeState] = useState()
const [anotherState, setAnotherSate] = useState()
...
const handleRemoveDetail = () => {
someState(0);
};
return (
{someState onRemoveDetail={handleRemoveDetail} ? (
<A />
) : (
<div>
<B />
<C />
</div>
)}
)
By conditionally rendering a component, you are mounting and unmounting it as the condition changes. So to answer your question, no, it will not retain its state values. State is kept during re-renders, but lost when the component unmounts.
However, if the state is managed by a parent component then we are talking about component A's props, not state. The props of component A will be the same each time it mounts and unmounts as long as the parent component has not changed them.
In addition, this syntax is incorrect, the callback should be provided like this:
return (
{someState ? (
<A onRemoveDetail={handleRemoveDetail} />
) : (
<div>
<B />
<C />
</div>
)}
)
A is not being 'visited', it is being rendered by its parent. The someState variable belongs to the parent component, and it will be available as long as the parent is retained. If you pass a setSomeState callback to any of the children and call it from there, the state in the parent will update.
Related
I am working on the project in React Typescript.
I have created hierarchy of components as per requirement.
In one scenario I have to pass data from child component to parent component and I am passing function as props and it works.
Issue :
When passing data to parent component child component gets re-render it looks like. Mean to say Dropdown selection is get reset and tree control expanded nodes get collapsed and set to the position as first time rendered.
I have used useState,useEffects hooks.
I have also tried React.memo as a part of my search on internet.
What I need :
I want to pass data to parent component from child without re-render the child component as there is no change in the props of child component.
Try this approach:
Add useCallback hook to memoize your function which lift data to <Parent />.
Then use React.memo for <Child /> to control prop changes and avoid unwanted re-renders.
I prepare an example for you here.
UPD. I have uploaded an example, you can copy it and see how it works!
Here is Child component:
const Child = ({ onChange }) => {
console.log("Child re-render");
return (
<div className="App">
<h1>Child</h1>
<button onClick={() => onChange(Math.random())}>
Lift value to Parant
</button>
</div>
);
};
const areEqual = ({ onChange: prevOnChange }, { onChange }) => {
return prevOnChange === onChange; // if true => this will avoid render
}
export default React.memo(Child, areEqual);
And the Parent:
consn App = () => {
const [value, setValue] = useState("");
const onChange = useCallback((value) => setValue(String(value)), []);
console.log("Parant re-render");
return (
<div className="App">
<h1>Parent</h1>
<div>Value is: {value}</div>
<Child onChange={onChange} />
</div>
);
}
Best regards 🚀
I found the following answer to a question that I currently have (link). In in, he said "If you pass any props to the component from the parent component and you update that prop in children or that prop update in the parent component so both will re-render." Can anyone give me a brief code snippet that achieves the case where you pass a prop to a child component from a parent component, then you update it in the child component so that both components will re-render? I am not still sure if this is attainable...
(scenario)
Let's say I have a parent component which is a sidebar containing a list of items. When I click an edit button of one of items, a modal (child component) where I can change the item name pops up. As hitting a submit button with a new item name as an input, the previous item name gets updated with the new one behind the scenes and the modal gets closed. As this happens, I would like the parent component to re-render to reflect the change on UI.
const Parent () => {
const [isOpen, setIsOpen] = useState(true);
const [{ items }, dipatch} = useStateValue(); //Fetch items object through React Context API
return (
{items.map((item) => (
<p>{item}<p />
))}
<Child callback={() => setIsOpen(false)} />
)
}
const Child (callback) => {
const submitHandler = () => {
postRequest(data); //updates items' data + updates items in the context
callback(); //After closing the modal here, I would like the parent component re-render.
}
return (
<Modal
isOpen={isOpen}
submit={submitHandler}
onClose={callback}
>...
<Modal />
)
}
Code example, when values from the parent are passed to the react child component, and data changes come from the child component.
Note that there is no state in the child component and it takes data from the values passed to it
import {useState} from 'react'
const NumberIn = ({num, change}) => {
return (
<input type='number' onChange={(e) => {
change(e.currentTarget.value)
}} value={num} />
)
}
export default function App() {
const [a, setA] = useState(0);
const [b, setB] = useState(0);
return (
<div className="App">
<NumberIn num={a} change={setA} /> + {' '}
<NumberIn num={b} change={setB} /> = {' '}
<span>{Number(a) + Number(b)}</span>
</div>
);
}
We pass a value and a function (a kind of callback) to the child component. When an event occurs in a child component, the passed function will be called, but it will be called in the parent component, because it belongs to the parent component, but with the parameters passed by the child component.
Currently I am facing the problem that I want to change a state of a child component in React as soon as a prop is initialized or changed with a certain value. If I solve this with a simple if-query, then of course I get an infinite loop, since the components are then rendered over and over again.
Component (parent):
function App() {
const [activeSlide, setActiveSlide] = useState(0);
function changeSlide(index) {
setActiveSlide(index);
}
return (
<div className="app">
<div className="app__nav">
<Button icon="FiSun" handler={changeSlide} active={activeSlide} index="0" />
<Button icon="FiSettings" handler={changeSlide} active={activeSlide} index="1" />
</div>
</div>
);
}
Component (child):
function Button(props) {
const Icon = Icons[props.icon];
const [activeClass, setActiveClass] = useState("");
// This attempts an endless loop
if(props.active == props.index) {
setActiveClass("active");
}
function toggleView(e) {
e.preventDefault();
props.handler(props.index);
}
return(
<button className={activeClass} data-index={props.index} onClick={toggleView}>
<Icon />
</button>
)
}
Is there a sensible and simple approach here? My idea would be to write the if-query into the return() and thus generate two different outputs, even though I would actually like to avoid this
The React docs have a nice checklist here used to determine if something does or does not belong in state. Here is the list:
Is it passed in from a parent via props? If so, it probably isn’t state.
Does it remain unchanged over time? If so, it probably isn’t state.
Can you compute it based on any other state or props in your component? If so, it isn’t state.
The active class does not meet that criteria and should instead be computed when needed instead of put in state.
return(
<button className={props.active == props.index ? 'active' : ''} data-index={props.index} onClick={toggleView}>
<Icon />
</button>
)
This is a great use of useEffect.
instead of the if statement you can replace that with;
const {active, index} = props
useEffect(_ => {
if(active == index) {
setActiveClass("active");
}
}, [active])
The last item in the function is a dependency, so useEffect will only run if the active prop has changed.
React automatically re-renders a component when there is a change in the state or props. If you're just using activeClass to manage the className, you can move the condition in the className as like this and get rid of the state.
<button className={props.active === props.index ? 'active' : ''} data-index={props.index} onClick={toggleView}>
<Icon />
</button>
however, if you still want to use state in the child component, you can use the useEffect hook to to update the state in the child component.
Try to use the hook useEffect to prevent the infinite loop. (https://fr.reactjs.org/docs/hooks-effect.html)
Or useCallback hook. (https://fr.reactjs.org/docs/hooks-reference.html#usecallback)
Try this and tell me if it's right for you :
function App() {
const [activeSlide, setActiveSlide] = useState(0);
const changeSlide = useCallback(() => {
setActiveSlide(index);
}, [index]);
return (
<div className="app">
<div className="app__nav">
<Button icon="FiSun" handler={changeSlide} active={activeSlide} index="0" />
<Button icon="FiSettings" handler={changeSlide} active={activeSlide} index="1" />
</div>
</div>
);
}
Say I have the following component named Home:
this.state = {
posts: []
}
componentDidMount() {
... data is fetched from api
}
<div
<PostForm />
{this.state.posts.map(post => (
<Post key={post.id} post={post} />
))}
</div>
How would I update the state of Home, or re fetch data from the api after the the PostForm component is submitted with a new Post.
Welcome to SO!
Setting the parent state from the child:
If you want your child component to have access to your parent component's state, just pass setState() as a prop in your parent class, like so...
<PostForm
setParentState={(state) => this.setState(state)}
/>
Then, later in PostForm.js, just set the parent state like so....
this.props.setParentState(newParentStateObject);
Or, you can even just do....
<PostForm
postform={this}
/>
And later on, you can call anything in postform with this.props.postform.anyFunctionEver().
Setting the child state from the parent:
Suppose you want to do the opposite now: update the state of the child component from the parent? That's just as easy, set a reference when defining <PostForm/>...
<PostForm
ref={(instance) => {this.postform = instance}}
/>
Then you can set the postform's state directly in your parent class...
this.postform.setState(newChildStateObject);
A lot can happen with the state, so if you're not sure, try making a testFunc() {console.log('test');}, and then try passing/activating this between parent and child.
I have two components: <Content /> as the parent, and <Curriculum /> as the child.
For the <Content /> component, after getting state from Redux store, I mapped from state to props and passed these props into the map() function.
// import...
class Content extends Component {
render() {
return (
<div>
{this.props.englishEntrances.map((english, i) => {
//Work
console.log(`CONTENT ${i}`);
console.log(this.props.curriculums);
console.log(this.props.curriculums[i]);
console.log(english);
return (
<Curriculum
key={i}
level={i + 1}
titleColor={colorLevels[i].title}
contentColor={colorLevels[i].content}
//Bug here
curriculum={this.props.curriculums[i]}
englishEntrance={english}
/>
);
})}
</div>
);
}
}
//const mapStateToProps...
export default connect(mapStateToProps, null)(Content);
Inside the map() function, there are two array of object: curriculums and englishEntrances.
I tried to print this.props.curriculums[i] and english, the console will appear these values normally.
The problem is: When i tried passing both values in return statement, in the <Curriculum /> component, only the english has the value, but this.props.curriculum is undefined.
Anyone know how to pass this.props value without getting undefined?
I forgot that the render() function in the life cycle of a React component can render right after initializing the constructor. In <Curriculum /> component, the value of this.props.curriculum will be undefined at this stage,
Therefore I have to handle event in that compoent before and after getting state from the store.