React Hoooks: change children state from parent - reactjs

I have a problem to change state for a child from the parent component.
Here is a sample sandbox.
https://codesandbox.io/s/parent-child-communication-hooks-34tjn?file=/src/components/Child.jsx
Here is my goal: When a user clicks "Turn on all button" in the Parent component, All of the children components will be turned on together. Turn off will turn off all children.
But Still, It is necessary to control each child.

Hi and welcome to StackOverflow. I see in your CodeSandbox that you started using React Hooks. Other than the useState hook we have the useEffect which will be triggered after the dependency has changed. You can achieve that by using this hook.
Simply add a new prop to your Child Component, this prop will contain the state value for the parent, we will call this parentValue. This is your Parent Component
//Parent.jsx
//your code
{childrentdata.map(x => (
<ChildComponent
name={x.name}
buttonOn={state.ParentOn}
parentValue={state.ParentOn}
/>
))}
Then, in your Child Component add the hook
useEffect(() => {
setState(prevState => ({ ...prevState, buttonOn: props.parentValue }));
}, [props.parentValue]);
The hook will watch for props and state changes that we specify in the array dependency as the second argument. So, everytime the parent toggle changes, it will change all the children. There is a change I made, if you notice I'm using a callback inside your setState, this is the way we can get the actual value for the state without getting a stale value. So, is a good practice to use this callback to prevent side effects.

Related

React functional component stops rendering on state changes after remounting

Any suggestions with this strange situation would be greatly appreciated.
I have a parent component that listens to the callback status from a child component, and renders a progress indicator based on the status. The child component is an editor and uses memo to prevent re-rendering. This is required by the editor package. Everything works fine when the parent and child are fist mounted, but if the parent is unmounted and remounted, it doesn't render the state changes triggered by the child callback.
I've tried various things to force a re-rendering, but nothing seems to work. I added a counter to the state and a delay on updating the state. I use ref as a value since the state is being accessed in a closure.
The child component is an editor that listens to websocket information and calls handleStatus callback asynchronously. The child component is working fine after the remount and sending the updates to the handleStatus callback.
Parent Component:
const [isConnected, setIsConnected] = useState<number>(0);
const forceUpdate = useRef<number>(0);
const statusRef = useRef('connected')
const handleStatus = (status: string) => {
if ('connected' !== status) {
forceUpdate.current += 1;
setTimeout(()=>{
setIsConnected(forceUpdate.current);
}, 100)
statusRef.current = status;
}
}
<div style={{width: '100%'}} >
{statusRef.current === 'connected' ? null : <CircularProgress />}
<ChildComponent key={roomId} roomId={roomId} handleStatus={handleStatus}/>
</div>
From the debugger, I can see that handleStatus is being called and setIsConnected is being called with a new value. But the parent does not render when the state is changed.
Again, the parent only stops listening to the state updates after it was remounted. On the first mount, the parent renders on every state update.
Any suggestions where to look would be greatly appreciated.
Edit:
I'm using the same key for the parent component, so react should remount the existing component instead of creating a new component. From what I can see, it does look like it's remounting the same component since the ref retains its value and the callback from the child component is still valid. The state value is stale, but that's expected since it's in a closure. The only thing that's not making sense is that I can't seem to trigger a rerender by updating the setState. I even tried a state function incase the setState was getting stale too, but that didn't work either.
you need to use useEffect and as dependency put statusRef variable
useEffect(() => {
}, [statusRef])
<ChildComponent key={roomId} roomId={roomId} handleStatus={handleStatus}/>

React: Modify Parent State While Child Renders

Hooks are usually much nicer than classes, so I'm trying to pass down the ability to set a parent's state in a child of that parent using hooks. Specifically, I'm doing something like this:
class Parent extends Component {
render = () => {
return <>
<Child useRegisterFoo={foo => {...;this.setState(foo);...;}} />
{ renderBasedOnRegister() }
</>
}
}
The goal is to be able to define the child like this:
const Child = ({useRegisterFoo}) => {
useRegisterFoo(1);
useRegisterFoo(2);
}
I will define many such children, and thus having such a simple useRegisterFoo to register a particular property of the child would be really useful.
The problem is that react complains if it's done this way, understandably, as I'm modifying the state of the parent while the parent is being rendered. Specifically it says:
Warning: Cannot update during an existing state transition (such as within `render`). Render methods should be a pure function of props and state.
What's a better approach at this problem? My main goal is to make the API from the perspective of the child as convenient as possible.
You are invoking the callbacks directly in the function body. This is an unintentional side-effect and shouldn't be done. Even in function components you still need to work with the component lifecycle.
Warning: Cannot update during an existing state transition (such as
within render). Render methods should be a pure function of props
and state.
The entire body of a function component is the "render" function. Use the useEffect hook to invoke these passed callbacks, or conditionally call them as part of an event handler.
Example:
Use an useEffect hook with an empty dependency array to trigger the effect only once when the component mounts.
const Child = ({useRegisterFoo}) => {
useEffect(() => {
useRegisterFoo(1);
useRegisterFoo(2);
}, []); // <-- empty dependency, effect triggered once on mount
...
}

React: Best way to set state in parent with value from child

First question here i go... I´ve been searching and couldn´t find the best practice.
My solution (Maybe stupid):
I have a parent form that has a child select.
const [periodo, setPeriodo] = useState('0')
const cambiarPeriodo=(e)=> { setPeriodo(e) }
<NewSelect value ={periodo} onchange={(e) => { cambiarPeriodo(e) }} />
The child function component select:
const periodosChange=(e)=> {
props.onchange(e.target.value);
}
<Select value={props.value} custom name="periodo" id="periodo" onChange={periodosChange} >
Options are populated via axios database query with a useEffect.
When user selects an option in list, fires a onchange callback function and set state in parent.
Because of setState, the parent and child reloaded and cleared the user selection. The solution was to pass via prop the state to the child and assign it in value of select. <Select value={props.value}..
Is there any better way to do it, or this is a standard procedure? Actually i would like the child not to reload at all because the select list wont ever change... Tried React.memo but couldn´t make it work
There is a class-based component lifecycle's hook called shouldComponentUpdate() which you can use to prevent the child component from re-rendering if the list won't change. But this means you will have to convert your child component from a functional component to a class-based component.
The other option would be to use useMemo() and memoize the entire component as shown here.

REACT: How do you set a child component's state to its parent's state?

I would like the child component's state to update as the parent component's state updates. Any changes to parent state variables should be reflected in the child's state. How can I do this?
Thanks!
Edit:
My components are arranged in the following manner. I would like to access the parent's state from Child 2. By setting the Child 1 state to the parent's state, I will be able to do this.
Parent->Child 1->Child 2
You can pass the part of the parent state that you need as a prop to the child.
Then each time the parent state will change, the child will rerender with the correct value.
If you need to change the state from within the child, it depends on the behaviour you want.
You can make the child change the parent state by passing a callback function as a prop (you can pass the function used to change the state in the parent as a prop to the child)
Or you can make the child local state beeing reset to the parent state when it changes by listening to changes on the prop with a useEffect or ComponentDidUpdate.
useEffect(() => { setState(props.partOfparentState)},[props.partOfparentState])
or
ComponentDidUpdate(prevProps) {
if(previousProps.partOfParentState != props.partOfParentState) {
partOfParentStatethis.setState({state:props.parpartOfParentStatetOfParentState})
}
}
You could also want other behaviour which could be achieved with a more complex useEffect.
State And Props
React DOM compares the element and its children to the previous one,
and only applies the DOM updates necessary to bring the DOM to the
desired state.
the data flow in react is “unidirectional” or “top-down”,
Any state is always owned by some specific component, and any data or
UI derived from that state can only affect components “below” them in
the tree.
If you imagine a component tree as a waterfall of props, each
component’s state is like an additional water source that joins it at
an arbitrary point but also flows down.
This is why the state is often called local or encapsulated. It is not
accessible to any component other than the one that owns and sets it. #reactjs
Therefore, You Should pass the state to the child component, also known as prop.
Edit:
the hierarchy will be like this:
App(state - date) => Child1(prop date, passing the same prop to the next child) => Child2(getting date from his parent - Child1)
Example (based on Class Components):
<App>:
The root of the app.
<ClockList date={this.state.date} />
<ClockList>:
notice we are using props
<Clock date={this.props.date} />
<Clock>:
import React from "react";
const Clock = (props) => {
// Object Destructuring can also be done in the function itself.
const { date } = props;
return (
<div className="container">
<p>{date}</p>
</div>
);
};
export default Clock;

React Hooks - prevent re-render, but act on the new props

I have a component that displays a web map. I need to pass data in and out of the map component, but I never need to re-render it. I do all my work on the map outside of react - working directly with the mapping library.
Using functional components and hooks, how can I pass props in, act on them, and get data out of this component without rerendering? I've tried a combo of useEffect() and memo(), but they end up canceling out each other's use:
I have useEffect watching for changes of the relevant props and memo blocking renders, but since memo blocks renders, and useEffect only fires after a render, I have no way of handling prop updates in this component.
const Map = memo(props => {
useEffect(() => {
// run this on props update
console.log(props.selectedImg);
}, [props.selectedImg]);
//... map lib specific code using props
return <div id="mapDiv" />;
},
//prevent all rerenders using memo..
() => true
);
This doesn't work because memo prevents re-render and so useEffect is never fired even though props are updated.
How can I use hooks to work with props while entirely preventing this component from re-rendering? Perhaps this structure is not correct at all.
Thanks for the help!

Resources