Issue
In the code below, I want successValue to be recalculated only when the state changes to 'success', and not 'fail' or 'loading' or 'init'. Therefore, I am providing state === success as dependency to useMemo:
import React, { useMemo, useEffect, useState } from 'react';
import { render } from 'react-dom';
type Status = 'init' | 'loading' | 'fail' | 'success'
const App = () => {
const [state, setState] = useState<Status>('init')
const successValue = useMemo(() => {
if (state === 'success') {
return 'success value'
}
return 'fallback value'
}, [state === 'success'])
useEffect(() => {
setTimeout(() => setState('success'), 1000)
}, [])
return (
<div>
<div>Status: {state}</div>
<div>Success value: {successValue}</div>
<button onClick={() => setState('loading')}>Loading</button>
<button onClick={() => setState('fail')}>Fail</button>
<button onClick={() => setState('success')}>Success</button>
</div>
)
}
render(<App />, document.getElementById('root'));
Here is an interactive demo: you will see that if you click 'fail' button for example, the successValue will change to fallback value - and I don't want that, I want it to stay as success value.
I think I misunderstood useMemo or how the dependencies work. Can someone provide and explanation and help me fix this?
Solution
I don't think useMemo alone will quite work for this use case because you want to recalculate it conditionally - but useMemo, if the dependency array changes, will unconditionally compute a new value.
Either store the prior value in state or a ref, then return that instead of returning 'fallback value', or do the calculation with useState / useEffect instead.
const { useState, useEffect } = React;
const App = () => {
const [state, setState] = useState('init')
const [successValue, setSuccessValue] = useState();
useEffect(() => {
if (state === 'success') {
setSuccessValue('success value');
}
}, [state === 'success']);
useEffect(() => {
setTimeout(() => setState('success'), 1000)
}, [])
return (
<div>
<div>Status: {state}</div>
<div>Success value: {successValue}</div>
<button onClick={() => setState('loading')}>Loading</button>
<button onClick={() => setState('fail')}>Fail</button>
<button onClick={() => setState('success')}>Success</button>
</div>
)
}
ReactDOM.createRoot(document.querySelector('.react')).render(<App />);
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<div class='react'></div>
Answered By - CertainPerformance
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.