Je me heurte à deux défis:
voir l'exemple de code ci-dessous:
class App extends Component {
constructor(props) {
super(props);
this.state = {
count: props.count > 100 ? 100 : props.count,
}
}
/*What is the equivalent implementation when React Hook is used here componentWillReceiveProps*/
componentWillReceiveProps(nextProps) {
if (nextProps.count !== this.props.count) {
this.setState({
count: nextProps.count > 100 ? 100 : nextProps.count
});
}
}
render() {
return ( <
div > {
this.state.count
} <
/div>
);
}
}
export default App;
Quant à componentDidUpdate, componentDidUpdate a sa partie routeur lorsque React Hook est utilisé, vous devez l'utiliser comme,
React.useEffect(() => {
return () => {
};
}, [parentProp]);
le deuxième paramètre pour useEffect s'assure que le code n'est exécuté que lorsque les accessoires changent, mais et si je veux faire des tâches respectives en fonction de plusieurs changements d'accessoires respectifs? comment faire avec useEffect?
voir l'exemple de code ci-dessous:
class App extends Component {
/*What is the equivalent implementation when functional component with React Hook is used here */
componentDidUpdate(prevProps, prevState) {
if (prevProps.groupName !== this.props.groupName) {
console.log('Let'
's say, I do want to do some task here only when groupName differs');
} else if (prevProps.companyName !== this.props.companyName) {
console.log('Let'
's say,I do want to do some different task here only when companyName differs');
}
}
render() {
/*for simplicity, render code is ignored*/
return null;
}
}
export default App;
Le hook réactif équivalent aux anciens accessoires componentWillReceive peut être fait en utilisant le crochet useEffect, en spécifiant simplement l'accessoire que nous voulons écouter pour les changements dans le tableau de dépendances.
Quelque chose comme:
export default (props) => {
useEffect( () => {
console.log('counter updated');
}, [props.counter])
return <div>Hi {props.counter}</div>
}
Pour componentDidUpdate simplement en omettant le tableau de dépendances, la fonction useEffect sera appelée après chaque rendu.
Vous pouvez utiliser le crochet useMemo
pour stocker un calcul et mettre props.count
dans le tableau donné comme deuxième argument pour recalculer la valeur lorsqu'elle change.
const { useState, useEffect, useMemo } = React;
function App() {
const [count, setCount] = useState(50);
useEffect(() => {
setTimeout(() => {
setCount(150);
}, 2000);
}, []);
return <DisplayCount count={count} />;
}
function DisplayCount(props) {
const count = useMemo(() => props.count > 100 ? 100 : props.count, [props.count]);
return <div> {count} </div>;
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
<div id="root"></div>
La façon la plus simple de faire des effets séparés lorsque des accessoires séparés changent est de créer plusieurs crochets useEffect
qui ne sont exécutés que lorsque l'un des accessoires séparés change.
const { useState, useEffect } = React;
function App() {
const [groupName, setGroupName] = useState('foo');
const [companyName, setCompanyName] = useState('foo');
useEffect(() => {
setTimeout(() => {
setGroupName('bar');
}, 1000);
setTimeout(() => {
setCompanyName('bar');
}, 2000);
}, []);
return <DisplayGroupCompany groupName={groupName} companyName={companyName} />;
}
function DisplayGroupCompany(props) {
useEffect(() => {
console.log("Let's say, I do want to do some task here only when groupName differs");
}, [props.groupName])
useEffect(() => {
console.log("Let's say,I do want to do some different task here only when companyName differs");
}, [props.companyName])
return <div> {props.groupName} - {props.companyName} </div>;
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
<div id="root"></div>
setCount déclenchera un nouveau rendu, en utilisant useEffect et le tableau de dépendances [count] spécifiera que seule "surveiller" la mise à jour de count lorsque la valeur de count est différente. Donc, cela peut être en quelque sorte remplacé componentWillReceiveProps. Nous pouvons dire que " chaque rendu a ses propres accessoires et états ". Si vous souhaitez déclencher le rendu dépend du changement d'accessoires, vous pouvez alors utiliser plusieurs effets.
/*componentWillReceiveProps*/
useEffect(() => {
count > 100 ? setCount(100) : setCount(count)
}, [count])
useEffect(() => {
console.log(`Let's say, I do want to do some task here only when groupName differs`);
}, [groupName])
useEffect(() => {
console.log(`Let''s say,I do want to do some different task here only when companyName differs`);
}, [companyName])
Dans votre scénario, vous n'avez pas du tout besoin d'utiliser ou de réimplémenter getDerivedStateFromProps
. Il vous suffit de créer une nouvelle variable pour obtenir la nouvelle forme de données. L'utilisation de state dans ce scénario provoquera simplement un nouveau rendu qui n'est pas bon en termes de performances.
import React from 'react';
const App = ({ count }) => {
const derivedCount = count > 100 ? 100 : count;
return (
<div>Counter: {derivedCount}</div>
);
}
App.propTypes = {
count: PropTypes.number.isRequired
}
Démo ici: https://codesandbox.io/embed/qzn8y9y24j?fontsize=14
Vous pouvez en savoir plus sur les différentes façons de résoudre ce type de scénarios sans utiliser getDerivedStateFromProps
ici: https://reactjs.org/blog/2018/06/07/you-probably-dont-need -derived-state.html
Si vous avez vraiment besoin d'utiliser un état séparé, vous pouvez utiliser quelque chose comme ça
import React, { useState } from 'react';
const App = ({ count }) => {
const [derivedCounter, setDerivedCounter] = useState(
count > 100 ? 100 : count
);
useEffect(() => {
setDerivedCounter(count > 100 ? 100 : count);
}, [count]); // this line will tell react only trigger if count was changed
return <div>Counter: {derivedCounter}</div>;
};
simplement en utilisant useEffect comme ceci.
useEffect( () => {
props.actions.fetchSignlePost(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.
Je me rends compte que votre exemple "d'état dérivé" est intentionnellement simple, mais comme il y a si peu de cas légitimes d'état dérivé, il est difficile de faire une recommandation sur le remplacement, sauf au cas par cas, car cela dépend de la raison pour laquelle vous utilisent l'état dérivé. Dans l'exemple particulier que vous avez fourni, il n'y avait aucune raison d'utiliser l'état dérivé dans le cas de classe et il n'y a donc toujours aucune raison dans le cas du hook (la valeur peut simplement être dérivée localement sans la mettre dans l'état). Si la valeur dérivée est coûteuse, vous pouvez utiliser useMemo
comme le présente Tholle. Si ceux-ci ne correspondent pas aux cas les plus réalistes que vous avez en tête, vous devrez présenter un cas plus spécifique qui nécessite vraiment un état dérivé.
En ce qui concerne votre exemple componentDidUpdate
, si ce que vous voulez faire pour les différents accessoires est indépendant, vous pouvez utiliser des effets séparés pour chacun (c'est-à-dire plusieurs appels useEffect
). Si vous voulez faire exactement ce qui est dans votre exemple (c'est-à-dire ne faire que quelque chose pour un companyName
changer si groupName
n'a pas également changé comme indiqué par votre else if
), vous pouvez alors utiliser refs pour des conditions plus sophistiquées. Vous ne devez pas ne pas muter la référence pendant le rendu (il y a toujours la possibilité que le rendu soit rejeté/refait une fois que le mode simultané est pris en charge), donc l'exemple utilise le dernier effet pour mettre à jour les refs. Dans mon exemple, j'utilise une référence pour éviter de faire un travail d'effet sur le rendu initial (voir la réponse de Tholle dans cette question connexe ) et pour détecter si groupName
a changé ou non pour décider si ou ne pas faire de travail basé sur un changement de 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>