Я понимаю, что ваш пример "производного состояния" преднамеренно прост, но поскольку существует очень мало законных случаев производного состояния, трудно дать рекомендацию о замене, кроме как в каждом конкретном случае, поскольку он зависит отпричина, по которой вы используете производное состояние.В приведенном вами конкретном примере не было никакой причины использовать производное состояние в случае класса, и поэтому в случае ловушки по-прежнему нет никаких причин (значение может быть просто получено локально, не переводя его в состояние).Если производное значение дорого, вы можете использовать useMemo
в качестве подарка Tholle.Если они не соответствуют более реалистичным случаям, которые вы имеете в виду, вам необходимо представить более конкретный случай, который действительно требует производного состояния.
Что касается вашего componentDidUpdate
примера, если чтоВы хотите сделать для разных реквизитов независимо, тогда вы можете использовать отдельные эффекты для каждого (например, несколько useEffect
вызовов).Если вы хотите сделать в точности , как в вашем примере (т.е. сделать что-то только для companyName
изменения, если groupName
также не изменилось, как указано вашим else if
), тогда вы можете использовать ссылки для более сложных условий.Вы должны не изменять ссылку во время рендеринга (всегда существует возможность сброса / повторного выполнения рендера, если поддерживается параллельный режим), поэтому в примере используется последний эффект для обновления ссылок.В моем примере я использую ссылку, чтобы не выполнять эффектную работу на начальном рендере (см. Ответ Толле в на этот связанный вопрос ) и чтобы определить, изменился ли groupName
при принятии решения, выполнять или нет работуосновано на companyName
изменении.
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>