私は2つの課題に直面します。
以下のコードサンプルを参照してください:
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;
ComponentDidUpdateについては、Reactフックが使用されている場合、componentDidUpdateはそれと同じ部分を持っています。
React.useEffect(() => {
return () => {
};
}, [parentProp]);
useEffectの2番目のパラメーターは、propの変更時にのみコードが実行されるようにしますが、複数のそれぞれのpropsの変更に基づいてそれぞれのタスクを実行したい場合?それを行う方法withEffect?
以下のコードサンプルを参照してください:
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;
古いcomponentWillReceiveプロップと同等のreactフックは、useEffectフックを使用して実行できます。依存関係配列の変更をリッスンするプロップを指定するだけです。
何かのようなもの:
export default (props) => {
useEffect( () => {
console.log('counter updated');
}, [props.counter])
return <div>Hi {props.counter}</div>
}
依存関係配列を省略しただけのcomponentDidUpdateの場合、再レンダリングのたびにuseEffect関数が呼び出されます。
useMemo
フックを使用して計算を保存し、props.count
2番目の引数として指定された配列で、値が変更されたときに値を再計算します。
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>
個別の小道具が変更されたときに個別の効果を実行する最も簡単な方法は、個別の小道具の1つが変更されたときにのみ実行される複数のuseEffect
フックを作成することです。
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は、useEffectを使用して再レンダリングをトリガーし、依存関係配列[count]は、カウントの値が異なる場合にカウントの更新を「監視」することだけを指定します。したがって、これは何らかの形でcomponentWillReceivePropsに置き換えることができます。 「 各レンダーには独自のプロパティと状態があります 」と言えます。プロップの変更に応じて再レンダリングをトリガーする場合は、複数のuseEffectを使用できます。
/*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])
シナリオでは、getDerivedStateFromProps
を使用したり再実装したりする必要はまったくありません。新しい形式のデータを取得するには、新しい変数を作成するだけです。このシナリオで状態を使用すると、別の再レンダリングが発生するだけで、パフォーマンスは良くありません。
import React from 'react';
const App = ({ count }) => {
const derivedCount = count > 100 ? 100 : count;
return (
<div>Counter: {derivedCount}</div>
);
}
App.propTypes = {
count: PropTypes.number.isRequired
}
ここのデモ: https://codesandbox.io/embed/qzn8y9y24j?fontsize=14
ここでgetDerivedStateFromProps
を使用せずにこれらの種類のシナリオを解決するさまざまな方法の詳細を読むことができます: https://reactjs.org/blog/2018/06/07/you-probably-dont-need -derived-state.html
本当に別の状態を使用する必要がある場合は、次のようなものを使用できます
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>;
};
このようなuseEffectを使用するだけです。
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.
あなたの「派生状態」の例は意図的に単純であることに気づきましたが、派生状態の正当なケースが非常に少ないため、理由に依存するため、ケースバイケースでの交換を推奨することは困難です。派生状態を使用しています。あなたが提供した特定の例では、クラスの場合に派生状態を使用する理由がなかったため、引き続きフックの場合には理由がありません(値を状態にせずにローカルに派生させることができます)。導出された値が高価な場合、Tholleが提示するようにuseMemo
を使用できます。これらが、より現実的なケースに当てはまらない場合は、派生状態を本当に必要とするより具体的なケースを提示する必要があります。
componentDidUpdate
の例に関しては、異なる小道具に対して実行したいことが独立している場合は、それぞれに個別の効果を使用できます(つまり、複数のuseEffect
呼び出し)。あなたがしたい場合正確にあなたの例には何がありますか(つまり、companyName
の場合のみgroupName
を変更しますelse if
)で示されるように変更されなかった場合は、より洗練された条件に refs を使用できます。レンダリング中にrefを変更しない必要があるため(同時モードがサポートされると、レンダリングが破棄/やり直される可能性が常にあります)、この例では、参照を更新する最後の効果。私の例では、最初のレンダリングで効果作業を行わないようにするためにrefを使用し( この関連する質問 のTholleの回答を参照)、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>