I need to pass value of state step from component Step to component App as I am going to pass that as a props to another child component (not presented in the code).
Initially, I tried to use lifting up, but could not synchronise step and count – count is less by 1.
The code is here: https://codesandbox.io/s/sparkling-violet-cn7glt
Then, I used useEffect hook,
code is here: https://codesandbox.io/s/kind-colden-pcthvq
and included step variable in dependency array, it worked but I suppose that the task might be solved by using only lifting states up without useEffect, would be grateful for your advices.
It might be a bit overwhelming to control your states in that way. Let’s consider to use Context component.
It allows to avoid heavy prop drilling and gives clearer and better way to control your states (in case when you do not want to use some third party State Management Systems).
Pseudocode
- Create Context Component
- Create Provider and pass your states via “value” attribute
- Inside child component import your Context and pass it into “useContext” hook
- Feel free to set/ use your states inside of any child inside Provider
Quick Example:
MyContext.js
import React from "react";
export default MyContext = React.createContext();
App.js
import { useState } from "react";
import MyChildComponent from "./components/MyChildComponent"
import MyContext from "./components/MyContext";
function App(){
const [count, setCount] = useState(0)
return(
<MyContext.Provider value={[count, setCount]}>
<MyChildComponent />
// notice that we do not drill any props. That is our context job to do
</MyContext.Provider>
)
MyChildComponent.js
import { useContext } from "react";
import MyContext from "./MyContext";
// notice that we need to import useContext hook and "MyContext" component
function MyChildComponent(){
const [count, setCount] = useContext(MyContext)
// from now on we have an access to the state we declared inside App.js file
// any changes you will do in here will affect all components which are using "count" state
const handler = () => {
setCount((prev)=>prev+1)
}
render(
<button onClick={handler}>{count}</button>
)
}
Also I made some small refactory of you code so go and have a look (starting from MyContext.js file). In there I wrote some comments below so you can follow the steps.
If you find yourself trying to synchronize two states with eachother, it’s often a sign that they shouldn’t be two states in the first place. Instead, just have one state. The child components should use the props they’re given directly and not copy them into states which then need to managed independently.
// App.js
function App() {
const [count, setCount] = useState(1);
const addCount = () => {
setCount(prev => prev + 1);
};
return (
<>
<span>App {count}</span>
<Step value={count} addCount={addCount} />
</>
);
}
// Step.js
function Step({ value, addCount }) {
return (
<>
<Plus value={value} addCount={addCount}>
<span>Step{value}</span>
</>
);
}
// Plus.js
function Plus({ value, addCount }) {
return <button onClick={addCount}>+</button>;
}
const [step, setStep] = useState(value);
What’s the reason you’re copying thevalue
prop into a local state variable?@NicholasTower I wanted to avoid setting initial state to 1 manually, I guess it is more convenient to set it from the only place.
Let me rephrase: What’s the reason you created two states (one in App and one in Step), instead of just one state (in App)? Are there any cases where you want the two states to be able to change independently from eachother?
@NicholasTower I followed a React course’s topic how to pass data to siblings, I need to lift up state from child Step to parent App, then pass as props to another child Count. There are all components in one App.js file, I tried to divide components in separate files but it doesn’t work as expected.