react-hooks/exhaustive-deps無視してない?
初めに
useEffect を用いて、レンダリング時と同期的に関数を実行させたい時に、依存関係を第二引数に入れないと linter がエラーを吐き出します。
しかし、アプリの動作に問題がない場合は、下記のようにして lint を無視するコードを書いてしまったことはあるのではないでしょうか?
基本的に、リンターは無視せずに、従うようにしましょう
useEffect(() => {
// ...
// 🔴 Avoid suppressing the linter like this:
// eslint-ignore-next-line react-hooks/exhaustive-deps
}, []);
では、どのように lint のエラーの解消を行なっていけばよいでしょうか?
以下の質問に答えてみて解決策を探してみてください。
そもそも useEffect 使わないとだめ?
最初に考えるべきことは、このコードが useEffect であるべきかどうかです。
イベントハンドラーでもいいのではないか?ということを検討しましょう。
Effect はユーザーの操作とは関係ない箇所で起こることですので、フォームを送信した時などのユーザー操作ではイベントハンドラーを使うようにしましょう。
useEffect 内で無関係なことをしていないか?
無関係なロジックをエフェクトに追加しないでください。
コード内の各効果は、別個の独立した同期プロセスを表す必要があります。
function ChatRoom({ roomId }) {
useEffect(() => {
logVisit(roomId);
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => {
connection.disconnect();
};
}, [roomId]);
// ...
}
たとえば、ユーザーが部屋を訪れたときに分析イベントを送信するとします。 roomId に依存する useEffect が既にあるので、そこに分析呼び出しを追加したくなるかもしれません。
しかし、後で接続を再確立する必要がある別の依存関係をこの Effect に追加することを想像してください。その時に、logVisit 関数も無関係ですが、呼び出されてしまいます。
訪問の記録は、接続とは別のプロセスです。それらを 2 つの個別のエフェクトとして記述します。
function ChatRoom({ roomId }) {
useEffect(() => {
logVisit(roomId);
}, [roomId]);
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
// ...
}, [roomId]);
// ...
}
状態を計算するために状態を読みっ取ってはいないか?
function ChatRoom({ roomId }) {
const [messages, setMessages] = useState([]);
useEffect(() => {
const connection = createConnection();
connection.connect();
connection.on('message', (receivedMessage) => {
setMessages([...messages, receivedMessage]);
});
return () => connection.disconnect();
}, [roomId, messages]); // ✅ All dependencies declared
// ...
message を受けとって新しい配列を作るために、message を読みって計算しています。
メッセージを受信するたびに、 setMessages() により、受信したメッセージを含む新しいメッセージ配列でコンポーネントが再レンダリングされます。
ただし、このエフェクトはメッセージに依存するようになったため、エフェクトも再同期されます。そのため、新しいメッセージがあるたびに、チャットが再接続されます。ユーザーはそれを望まないでしょう!
下記のような形で、message state を使用しなくても記述することができます。不用意な useEffect の実行を防ぐことができます。
function ChatRoom({ roomId }) {
const [messages, setMessages] = useState([]);
useEffect(() => {
const connection = createConnection();
connection.connect();
connection.on('message', (receivedMessage) => {
setMessages(msgs => [...msgs, receivedMessage]);
});
return () => connection.disconnect();
}, [roomId]); // ✅ All dependencies declared
// ...
最後に
下記で凡例を示してくれてます。
Discussion