React: Modify Parent State While Child Renders - reactjs

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
...
}

Related

How to access data of Child Component from parent component in react js

I am doing a project where i have a toast function which implements toast there i call the function of fetching data from api and updating my state so that whenever i click the update feed button fetching data from api function called, updation of state and toast of success appears. Now the question is i have a component of categories post displays seperate category post inside of all post component which has the function to display toast, how could i pass the updated state,fetching data from api function from child component that is category post component to parent component that is all post component to implement toast for category component.
If I understand your question correctly -- at a high level, you're trying to figure out how to update a state variable of a parent component from within a child component. Easiest way would be with the useState hook, and then by passing the setState function to the child component.
const ParentComponent = () => {
const [state, setState] = useState([])
useEffect(() => {
// logic that will be executed every time the state variable changes
}, [state])
return <ChildComponent setState={setState} />
}
const ChildComponent = ({setState}) => {
const handleClick = () => {
setState((currentState) => currentState.concat(1))
}
return <Button onClick={handleClick} />
}
Edit: To answer your question from the comment -- a few things to point out here:
You can pass a value to useState which will be the starting value of the variable. In our example, it's an empty array
setState has access to the current state, so you can push a value to an array with this syntax: setState((previousState) => previousState.concat(val))
useEffect is a hook which is invoked whenever there's a change in the value of the dependency (or dependencies) passed in the second argument. So by including state in its dependency array, we can execute whatever logic we want every time the value of the state variable changes
I would also recommend looking into useMemo. It similarly allows you to have aspects of your component logic that are re-executed only when values of certain variables change. For example:
const ParentComponent = () => {
const [state, setState] = useState([])
useEffect(() => {
// logic that will be executed every time the state variable changes
}, [state])
const renderCards = useMemo(() => {
return state.map(val => <SomeOtherComponent val={val}/>)
}, [state])
return (
<div>
{renderCards}
<ChildComponent setState={setState} />
</div>
)
}
By wrapping the function inside renderCards in the useMemo hook, the evaluated result is "memoized". And so it won't be executed on every render, unless the variable in the dependency array changes.
Passing down setState to a child component in order to trigger a re-render in the parent component is straightforward when it's an immediate child. If the child component is nested deeper, or there are multiple components that need to react to a change in a variable (e.g. light/dark mode) -- that's when you want to look into a state management tool like Redux or Context.
There are two ways I can think of to achieve what you are trying to do here, i.e. get the child component's state in a parent component.
You can make use of refs. You should be familiar with React hooks to use this approach. The documentation is really good.
You can also make use of a global state using either Redux or Context.

React: How to pass state to child component and call function in child to use this state

I am implementing a component in functional components where there are several other child components in it passing data to each other. I need to pass data from parent to child component and call some function there to use it.
In class componenets we use componentdidupdate but could not understand how to do in functional component.
One idea is to use useEffect hook but could not do with it.
Im going to take a stab here, because we dont have context or code to go with.
useEffect accepts a dependency array to which it will react when a value or object reference changes
const ChildComponent = (props) => {
const {
valuePassedFromParent
} = props;
const actionFunction = (value) => {
//perform some tasks with value passed from parent when it changes
}
//this is similar to componentDidUpdate, but it reacts to changes in valuePassedFromParent though props since its in the useEffect dependency array
useEffect(() => {
//do something with valuePassedFromParent
actionFunction(valuePassedFromParent);
},[valuePassedFromParent]);
return (
<div>
</div>
)
}
You cas use useEffect to reproduce the behavior of componentdidupdate like this :
const [myState,setMyState] = useState();
useEffect(() => {
...
},[myState]);
The function use effect will run every time myState will be updated, like componentdiduptate would have do.
In your case, the state is given by the parent component if I understand well, so just replace the myState in the array dependency by the state given through the prop of your child component.

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 Hoooks: change children state from parent

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.

Is there a way to make sure child components fire actions after parent components?

I'm having issues when I try to fire an action within the useEffect hook defined at the entry point for my application. Lower in the component tree, I'm using the useEffect hook to make API calls that use a crucial piece of data being retrieved in the entry point I described. It seems that I would not want to move the fetching of that crucial piece of data down and into every child component that needs it. Am I using an anti-pattern here?
The only other option I can think of is not calling the API until that piece of data is retrieved, but for reusability purposes this is not a good way of doing things and provides a lot of potential issues.
The component tree is as follows:
<App> <-- useEffect hook retrieves crucial piece of data
<Child /> <-- useEffect hook calls API with data
</App>
An error occurs because the route gets called without that crucial piece of data, but I have it set up to call the API again once that data is received. This is not a good solution and wastes another API call.
You have to do next.
Create context to provide information about parent initialization to child components.
const ParentInitializeContext = React.createContext(false);
Create a boolean state variable isInitialized in the parent component and set it to true when the parent component will initialize. You also have to provide isInitialized to context:
function App(props) {
const [isInitialized, setIsInitialized] = useState(false);
useEffect(() => {
//here you retreive a crucial piece of data and then call setIsInitialized(true);
}, []);
return (
<ParentInitializeContext.Provider value={isInitialized}>
{props.children}
</ParentInitializeContext.Provider>
);
}
Create own hook to call functions when parent component will initialize:
function useEffectWhenParentInitialized(fn, inputs = []) {
const isParentInitialized = useContext(ParentInitializeContext);
useEffect(() => {
if (isParentInitialized) {
fn();
}
}, [...inputs, isParentInitialized]);
}
Change useEffect hook in child components to useEffectWhenParentInitialized.
You can view a full example here: https://codesandbox.io/s/5volk3o2ol

Resources