Reputation: 41
here is the code inside useEffect in component, I want to update these states after the first render. I want to obtain my goal without dependencies warning.
useEffect(() => {
setQualification({ ...qualification, options: handleOptionToData("Qualification") })
setWorkingWith({ ...workingWith, options: handleOptionToData("Working with") })
setAnnualIncome({ ...annualIncome, options: handleOptionToData("Income") })
setProfessionArea({ ...professionArea, options: handleOptionToData("Profession area") })
setOptionsData(true)
}, [])
and for that i got these warning:
React Hook useEffect has missing dependencies: 'annualIncome', 'handleOptionToData', 'professionArea', 'qualification', and 'workingWith'. Either include them or remove the dependency array. You can also do a functional update 'setWorkingWith(w => ...)' if you only need 'workingWith' in the 'setWorkingWith' call react-hooks/exhaustive-deps
Upvotes: 1
Views: 856
Reputation: 700
The answer from @Imanpal-Singh is correct, but to expand a little on it...
As the Warning from ESLint stated, you can convert those into arrow functions which receive the old state.
The return value of the function will be used to set the new state.
This means your code becomes something like this:
useEffect(() => {
setQualification(currentState=> ({ ...currentState, options: handleOptionToData("Qualification") }))
setWorkingWith(currentState=> ({ ...currentState, options: handleOptionToData("Working with") }))
setAnnualIncome(currentState=> ({ ...currentState, options: handleOptionToData("Income") }))
setProfessionArea(currentState=> ({ ...currentState, options: handleOptionToData("Profession area") }))
setOptionsData(true)
}, [])
Which is very close to your original code, and shouldn't trigger the ESLint warning.
Here's an example:
// Get a hook function
const {useState, useEffect, useRef} = React;
const Example = ({title}) => {
const [myState, setMyState] = useState({count:0, status:'Loading'});
useEffect(() => {
setMyState(originalValue => ({...originalValue, status:'Loaded'}));
}, [])
// count number of renders
const renderCounter = useRef(0);
renderCounter.current = renderCounter.current + 1;
return (
<div>
<p>{title} - {myState.status} </p>
<p>You clicked {myState.count} times</p>
<button onClick={() => setMyState({...myState, count:myState.count+1})}>
Click me
</button>
<p> Renders: {renderCounter.current} </p>
</div>
);
};
// Render it
ReactDOM.createRoot(
document.getElementById("root")
).render(
<Example title="Example using Hooks:" />
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.development.js"></script>
<div id="root"></div>
However
I would really, really try to remove any setState
from useEffect
as much as you can.
I've started to think of it as a code-smell.
You don't state what handleOptionToData
does, but if it's just processing data which has been passed into the component, move that into the useState
initialisation.
// Get a hook function
const {useState, useEffect, useRef} = React;
const processData = (data)=> {
//This function processes the data so we don't have to do so after first render.
return {...data, status:'Loaded'};
}
const Example = ({title, propData}) => {
const [myState, setMyState] = useState(processData(propData));
// count number of renders
const renderCounter = useRef(0);
renderCounter.current = renderCounter.current + 1;
return (
<div>
<p>{title} - {myState.status} </p>
<p>You clicked {myState.count} times</p>
<button onClick={() => setMyState({...myState, count:myState.count+1})}>
Click me
</button>
<p> Renders: {renderCounter.current} </p>
</div>
);
};
// Render it
ReactDOM.createRoot(
document.getElementById("root")
).render(
<Example title="Example using Hooks:" propData={{count:0, status:'Loading'}} />
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.development.js"></script>
<div id="root"></div>
You'll notice that this version doesn't need the extra render, and has the same end result.
Reducing extra renders will speed up your application.
React 18 Warning
React 18 can run certain hooks multiple times!
This is deliberate to flush out bugs in your code.
The function method to update state won't cause any issues with this multiple call of hooks, but using the state variable could potentially cause issues (there's a reason why ESLint is showing a warning!).
Upvotes: 0
Reputation: 1197
You can use this syntax to avoid dependency on the state,
useEffect(()=>{
setState(prev=>*your logic*)
},[setState])
This way you can use prev to update new state and your state
will no longer be a dependency of the useEffect
and will not cause an infinite loop.
Upvotes: 3
Reputation: 525
Add the dependency on dependencies array like this
const [data , setData] = useState({
qualification : {},
workingWith : {},
annualIncome : {},
professionArea : {}
})
useEffect(() => {
const newData = {
...data,
qualification : { ...data.qualification , options: handleOptionToData("Qualification") },
workingWith : { ...data.workingWith , options: handleOptionToData("workingWith") },
annualIncome : { ...data.annualIncome , options: handleOptionToData("annualIncome") },
professionArea : { ...data.professionArea , options: handleOptionToData("Profession area") },
}
setData(newData)
setOptionsData(true)
}, [qualification, workingWith , annualIncome , professionArea])
Upvotes: 1