I run into two challenges:
simply by using useEffect like this.
useEffect( () => {
props.actions.fetchSinglePost(props.match.params.id); //> I'm dispatching an action here.
}, [props.comments]) //> and here to watch comments and call the action in case there is any change.
I realize your "derived state" example is intentionally simple, but because there are so few legitimate cases of derived state, it is difficult to make a recommendation on the replacement except on a case-by-case basis since it depends on the reason you are using derived state. In the particular example you provided, there was no reason to use derived state in the class case and so there continues to be no reason in the hook case (the value can just be derived locally without putting it in state). If the derived value is expensive, you can use useMemo
as Tholle presents. If these don't fit the more realistic case(s) you have in mind, you would need to present a more specific case that truly requires derived state.
As far as your componentDidUpdate
example, if what you want to do for the different props is independent, then you can use separate effects for each (i.e. multiple useEffect
calls). If you want to do exactly what is in your example (i.e. only do something for a companyName
change if groupName
didn't also change as indicated by your else if
), then you can use refs for more sophisticated conditions. You should not mutate the ref during rendering (there is always the possibility of the render being discarded/redone once concurrent mode is supported), so the example uses the last effect to make updates to the refs. In my example, I use a ref to avoid doing effect work on the initial render (see Tholle's answer in this related question) and to detect whether or not groupName
changed when deciding whether or not to do work based on a companyName
change.
const { useState, useEffect, useRef } = React;
const DerivedStateFromProps = ({ count }) => {
const derivedCount = count > 100 ? 100 : count;
return (
<div>
Derived from {count}: {derivedCount}{" "}
</div>
);
};
const ComponentDidUpdate = ({ groupName, companyName }) => {
const initialRender = useRef(true);
const lastGroupName = useRef(groupName);
useEffect(
() => {
if (!initialRender.current) {
console.log("Do something when groupName changes", groupName);
}
},
[groupName]
);
useEffect(
() => {
if (!initialRender.current) {
console.log("Do something when companyName changes", companyName);
}
},
[companyName]
);
useEffect(
() => {
if (!initialRender.current && groupName === lastGroupName.current)
console.log(
"Do something when companyName changes only if groupName didn't also change",
companyName
);
},
[companyName]
);
useEffect(
() => {
// This effect is last so that these refs can be read accurately in all the other effects.
initialRender.current = false;
lastGroupName.current = groupName;
},
[groupName]
);
return null;
};
function App() {
const [count, setCount] = useState(98);
const [groupName, setGroupName] = useState("initial groupName");
const [companyName, setCompanyName] = useState("initial companyName");
return (
<div>
<div>
<DerivedStateFromProps count={count} />
<button onClick={() => setCount(prevCount => prevCount + 1)}>
Increment Count
</button>
</div>
<div>
<ComponentDidUpdate groupName={groupName} companyName={companyName} />
groupName:{" "}
<input
type="text"
value={groupName}
onChange={event => setGroupName(event.target.value)}
/>
<br />
companyName:{" "}
<input
type="text"
value={companyName}
onChange={event => setCompanyName(event.target.value)}
/>
<br />
change both{" "}
<input
type="text"
onChange={event => {
const suffix = event.target.value;
setGroupName(prev => prev + suffix);
setCompanyName(prev => prev + suffix);
}}
/>
</div>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<div id="root"></div>
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>