ライフサイクルメソッドをフックに反応させる
記事付きの簡単なブログがあります。そして、クラスを機能コンポーネントとフックに書き直したいと思います。これで、編集/追加フォームを使用して、ページのライフサイクルメソッドでこのロジックを取得しました。正常に機能します。
componentDidUpdate(prevProps, prevState) {
if (this.props.match.params.id !== prevProps.match.params.id) {
if (prevProps.match.params.id) {
this.props.onUnload();
}
this.id = this.props.match.params.id;
if (this.id) {
return this.props.onLoad(userService.articles.get(this.id));
}
this.props.onLoad(null);
}
this.isLoading = false;
}
componentDidMount() {
if (this.id) {
this.isLoading = true;
return this.props.onLoad(userService.articles.get(this.id));
}
this.isLoading = false;
this.props.onLoad(null);
}
componentWillUnmount() {
this.props.onUnload();
}
shouldComponentUpdate(newProps, newState) {
if (this.props.match.params.id !== newProps.match.params.id) {
this.isLoading = true;
}
return true;
}
私はそれをすべてそのようなフックに書き直しました:
//componentDidMount
useEffect(() => {
if (id) {
setIsloading(true);
return props.onLoad(userService.articles.get(id));
}
setIsloading(false);
props.onLoad(null);
}, []);
useEffect(()=> {
prevId.current = id;
}, [id]
);
//componentDidUpdate
useEffect(() => {
//const id = props.match.params.id;
if (id !== prevId.current) {
if (prevId.current) {
props.onUnload();
}
if (id) {
return props.onLoad(userService.articles.get(id));
}
props.onLoad(null);
}
setIsloading(false);
});
//componentWillUnmount
useEffect(() => {
return props.onUnload();
}, []);
- 「再レンダリングが多すぎます」というエラーが発生しました。codesandboxで完全なコード:codesandbox
奇妙ですが、ローカルホストでは「再レンダリングが多すぎます」というエラーはありません。
クラス「shouldComponentUpdate」メソッドをフックに書き換える方法がわかりません。「メモ」を試しましたが、この場合の書き方がわかりません。
そしてとにかく、それはすべて機能しないので、私は何かが欠けています-それはフォームフィールドを適切に更新していません。
あなたがreactフックについての十分な知識を持っているなら、助けてください、またはいくつかのアドバイスを与えてください-それを修正する方法は?
回答
依存関係のない効果が原因"Too many re-renders."
です:レンダリングのたびに実行され、次にsetIsLoading
state(loading
)を更新するために呼び出されます。これにより、コンポーネントが再レンダリングされ、effect
再度実行され、が再度setState
呼び出さeffect
れます。
//componentDidUpdate
useEffect(() => {
//const id = props.match.params.id;
if (id !== prevId.current) {
if (prevId.current) {
props.onUnload();
}
if (id) {
return props.onLoad(userService.articles.get(id));
}
props.onLoad(null);
}
setIsloading(false);
})
問題を修正するには、を削除するか、依存関係としてsetIsLoading
追加IsLoading
します。
//componentDidUpdate
useEffect(() => {
...
setIsloading(false);
},[isLoading]);
2つのマウントエフェクトを次のように1つにマージすることもできます(あなたも機能していますが、これはスタイル的に好ましいと思います):
//componentDidMount
useEffect(() => {
if (id) {
setIsloading(true);
return props.onLoad(userService.articles.get(id));
}
setIsloading(false);
props.onLoad(null);
//componentWillUnmount
return function () {
props.onUnload()
}
}, []);
2番目の箇条書きの場合:コンポーネントの書き換えについてshouldComponentUpdate
; 最初にshouldComponentUpdate
、あなたは常にを返しtrue
、loading
状態をトリガーするためにのみそれを使用しているので、あなたの既存のものは合理的に聞こえないことを指摘しなければなりません。また、React.memo(shouldComponentUpdate
クラスコンポーネントと同等)で作成することはできません。したがって、loading
状態を判断するために必要なのは、すべての小道具の変更で実行する必要があるだけです。そのために、次のprops
ように依存関係としてエフェクトを使用できます。
//manually keeping old props id
const prevPropsId = useRef();
useEffect(() => {
if (prevPropsId.current !== props.match.params.id) {
setIsLoading(true);
}
prevPropsId.current = props.match.params.id;
}, [props.match.params.id]);
// this hook only run if `props.match.params.id` change
私の認識に基づいて、これは仕事をします(そもそもなぜこのようにロジックを書くのか理解するのに苦労しているにもかかわらず)そしてそれがうまくいかない場合はあなたのニーズに合うようにロジックを少し調整するかもしれません、あなたは小道具の変更効果がどのように機能するかを考えます。また、あなたは多くの処理する必要がありますtypeError
場合id
、params
またはmatch
存在せず、任意の連鎖はここに便利なツールをすることができます- >props.match?.params?.id
古いライフサイクルフックを見逃した場合は、いつでもカスタムフックとして再作成できます。
function useComponentDidMount(effect) {
useEffect(() => {
effect();
}, []);
}
function useComponentWillUnmount(effect) {
useEffect(() => {
return effect;
}, []);
}
function useComponentDidUpdate(effect, dependencies) {
const hasMountedRef = useRef(false);
const prevDependenciesRef = useRef(null)
useEffect(() => {
// don't run on first render, only on subsequent changes
if (!hasMountedRef.current) {
hasMountedRef.current = true;
prevDependenciesRef.current = dependencies;
return;
}
// run effect with previous dependencies, like in componentDidUpdate!
effect(...prevDependenciesRef.current || []);
prevDependenciesRef.current = dependencies;
}, dependencies)
}
使用法(リファクタリングされた例):
//componentDidMount
useComponentDidMount(() => {
if (id) {
setIsloading(true);
props.onLoad(userService.articles.get(id));
return;
}
setIsloading(false);
props.onLoad(null);
});
//componentDidUpdate
useComponentDidUpdate((prevId) => {
if (prevId) {
props.onUnload();
}
if (id) {
props.onLoad(userService.articles.get(id));
return;
}
props.onLoad(null);
setIsloading(false);
}, [id]);
//componentWillUnmount
useComponentWillUnmount(() => {
props.onUnload();
});