1640668102
ReactのuseEffect
クリーンアップ機能は、エフェクトをクリーンアップすることにより、メモリリークなどの望ましくない動作からアプリケーションを保護します。そうすることで、アプリケーションのパフォーマンスを最適化できます。
この記事を始めるにuseEffect
は、APIをフェッチするために使用することを含め、何であるかについての基本的な理解が必要です。この記事では、useEffect
フックのクリーンアップ機能について説明します。この記事の終わりまでに、クリーンアップ機能を快適に使用できるようになることを願っています。
useEffect
クリーンアップ機能とは何ですか?名前が示すように、useEffect
クリーンアップはフックの関数であり、コンポーネントがアンマウントされる前にコードを整理することができます。コードが実行され、レンダリングごとに再実行されると、クリーンアップ関数を使用してコード自体もクリーンアップされます。useEffect
useEffect
useEffect
フックは、我々はその中の関数を返すことができますし、クリーンアップが起こるところ、このリターン機能があるような方法で構築されています。クリーンアップ機能は、メモリリークを防ぎ、不要で不要な動作を削除します。
return関数内の状態も更新しないことに注意してください。
useEffect(() => {
effect
return () => {
cleanup
}
}, [input])
useEffect
クリーンアップ機能が役立つのはなぜですか?前述のように、useEffect
クリーンアップ機能は、開発者が不要な動作を防ぎ、アプリケーションのパフォーマンスを最適化する効果をクリーンアップするのに役立ちます。
ただし、useEffect
クリーンアップ関数は、コンポーネントのマウントを解除するときに実行されるだけでなく、次にスケジュールされたエフェクトの実行の直前にも実行されることに注意してください。
実際、エフェクトが実行された後、次にスケジュールされるエフェクトは通常、:に基づいています。dependency(array)
// The dependency is an array
useEffect( callback, dependency )
したがって、効果がプロップに依存している場合、または持続するものを設定する場合はいつでも、クリーンアップ関数を呼び出す理由があります。
このシナリオを見てみましょう。ユーザーのを介して特定のユーザーid
のフェッチを取得し、フェッチが完了する前に、考えを変えて別のユーザーを取得しようとするとします。この時点で、id
前のフェッチ要求がまだ進行中の間に、小道具、この場合は、が更新されます。
次に、クリーンアップ関数を使用してフェッチを中止し、アプリケーションがメモリリークにさらされないようにする必要があります。
useEffect
クリーンアップはいつ使用する必要がありますか?データをフェッチしてレンダリングするReactコンポーネントがあるとしましょう。promiseが解決する前にコンポーネントがアンマウントされると、useEffect
(マウントされていないコンポーネントの)状態を更新しようとし、次のようなエラーを送信します。
このエラーを修正するには、クリーンアップ機能を使用してエラーを解決します。
Reactの公式ドキュメントによると、「Reactは、コンポーネントがアンマウントされたときにクリーンアップを実行します。ただし…エフェクトは、1回だけでなく、すべてのレンダリングに対して実行されます。これが、Reactが次回エフェクトを実行する前に、前のレンダリングのエフェクトもクリーンアップする理由です。」
クリーンアップは通常、行われたすべてのサブスクリプションをキャンセルし、フェッチ要求をキャンセルするために使用されます。それでは、コードを書いて、これらのキャンセルをどのように実行できるかを見てみましょう。
サブスクリプションのクリーンアップを開始するには、最初にサブスクライブを解除する必要があります。これは、アプリをメモリリークにさらしたくないため、アプリを最適化するためです。
私たちのコンポーネントアンマウント前に、当社のサブスクリプションから退会するには、私たちの変数を設定し、聞かせてisApiSubscribed
、とtrue
し、我々はそれを設定することができfalse
、我々はアンマウントしたいとき:
useEffect(() => {
// set our variable to true
let isApiSubscribed = true;
axios.get(API).then((response) => {
if (isApiSubscribed) {
// handle success
}
});
return () => {
// cancel the subscription
isApiSubscribed = false;
};
}, []);
上記のコードでは、変数を設定isApiSubscribed
しtrue
た後、私たちの成功の要求を処理するための条件としてそれを使用します。ただし、コンポーネントをアンマウントisApiSubscribed
するfalse
ときに変数をに設定します。
フェッチリクエストの呼び出しをキャンセルするには、さまざまな方法があります。AxiosのキャンセルトークンAbortController
を使用するか、使用します。
を使用するAbortController
には、コンストラクターを使用してコントローラーを作成する必要があります。次に、フェッチリクエストが開始されると、リクエストのオブジェクト内にオプションとして渡されます。AbortController()AbortSignaloption
これにより、コントローラーとシグナルがフェッチリクエストに関連付けられ、次を使用していつでもキャンセルできます。AbortController.abort()
>useEffect(() => {
const controller = new AbortController();
const signal = controller.signal;
fetch(API, {
signal: signal
})
.then((response) => response.json())
.then((response) => {
// handle success
});
return () => {
// cancel the request before component unmounts
controller.abort();
};
}, []);
さらに進んで、catchにエラー条件を追加して、中止したときにフェッチ要求がエラーをスローしないようにすることができます。このエラーが発生するのは、アンマウント中に、エラーを処理するときに状態を更新しようとするためです。
私たちにできることは、条件を記述して、どのようなエラーが発生するかを知ることです。アボートエラーが発生した場合、状態を更新したくありません。
useEffect(() => {
const controller = new AbortController();
const signal = controller.signal;
fetch(API, {
signal: signal
})
.then((response) => response.json())
.then((response) => {
// handle success
console.log(response);
})
.catch((err) => {
if (err.name === 'AbortError') {
console.log('successfully aborted');
} else {
// handle error
}
});
return () => {
// cancel the request before component unmounts
controller.abort();
};
}, []);
これで、リクエストが解決する前に焦って別のページに移動した場合でも、コンポーネントがアンマウントされる前にリクエストが中止されるため、そのエラーが再び発生することはありません。アボートエラーが発生した場合、状態も更新されません。
それでは、AxiosのキャンセルオプションであるAxiosキャンセルトークンを使用して同じことを行う方法を見てみましょう。
まず、from Axiosをsourceという名前の定数に格納し、トークンをAxiosオプションとして渡し、次のコマンドでいつでもリクエストをキャンセルします。CancelToken.source()source.cancel()
useEffect(() => {
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
axios
.get(API, {
cancelToken: source.token
})
.catch((err) => {
if (axios.isCancel(err)) {
console.log('successfully aborted');
} else {
// handle error
}
});
return () => {
// cancel the request before component unmounts
source.cancel();
};
}, []);
AbortError
inAbortController
で行ったのと同じように、AxiosはisCancel
、エラーの原因を確認し、エラーの処理方法を知ることができるというメソッドを提供します。
Axiosソースが中止またはキャンセルされたためにリクエストが失敗した場合、状態を更新する必要はありません。
useEffect
クリーンアップ機能の使い方上記のエラーが発生する可能性がある場合の例と、発生した場合のクリーンアップ関数の使用方法を見てみましょう。2つのファイルを作成することから始めましょう:Post
とApp
。次のコードを記述して続行します。
// Post component
import React, { useState, useEffect } from "react";
export default function Post() {
const [posts, setPosts] = useState([]);
const [error, setError] = useState(null);
useEffect(() => {
const controller = new AbortController();
const signal = controller.signal;
fetch("https://jsonplaceholder.typicode.com/posts", { signal: signal })
.then((res) => res.json())
.then((res) => setPosts(res))
.catch((err) => setError(err));
}, []);
return (
<div>
{!error ? (
posts.map((post) => (
<ul key={post.id}>
<li>{post.title}</li>
</ul>
))
) : (
<p>{error}</p>
)}
</div>
);
}
これは、すべてのレンダリングで投稿を取得し、フェッチエラーを処理する単純な投稿コンポーネントです。
ここでは、メインコンポーネントに投稿コンポーネントをインポートし、ボタンをクリックするたびに投稿を表示します。ボタンは投稿を表示および非表示にします。つまり、投稿コンポーネントをマウントおよびアンマウントします。
// App component
import React, { useState } from "react";
import Post from "./Post";
const App = () => {
const [show, setShow] = useState(false);
const showPost = () => {
// toggles posts onclick of button
setShow(!show);
};
return (
<div>
<button onClick={showPost}>Show Posts</button>
{show && <Post />}
</div>
);
};
export default App;
ここで、ボタンをクリックし、投稿がレンダリングされる前にもう一度ボタンをクリックすると(別のシナリオでは、投稿がレンダリングされる前に別のページに移動する場合があります)、コンソールでエラーが発生します。
これは、ReactuseEffect
がまだ実行中であり、バックグラウンドでAPIをフェッチしようとしているためです。APIのフェッチが完了すると、状態を更新しようとしますが、今回はマウントされていないコンポーネントであるため、次のエラーがスローされます。
ここで、このエラーをクリアしてメモリリークを停止するには、上記のソリューションのいずれかを使用してクリーンアップ機能を実装する必要があります。この投稿では、以下を使用しますAbortController
:
// Post component
import React, { useState, useEffect } from "react";
export default function Post() {
const [posts, setPosts] = useState([]);
const [error, setError] = useState(null);
useEffect(() => {
const controller = new AbortController();
const signal = controller.signal;
fetch("https://jsonplaceholder.typicode.com/posts", { signal: signal })
.then((res) => res.json())
.then((res) => setPosts(res))
.catch((err) => {
setError(err);
});
return () => controller.abort();
}, []);
return (
<div>
{!error ? (
posts.map((post) => (
<ul key={post.id}>
<li>{post.title}</li>
</ul>
))
) : (
<p>{error}</p>
)}
</div>
);
}
クリーンアップ関数でシグナルを中止した後でも、アンマウントによってエラーがスローされることがコンソールに表示されます。前に説明したように、このエラーはフェッチ呼び出しを中止したときに発生します。
useEffect
catchブロックでフェッチエラーをキャッチしてから、エラー状態を更新しようとすると、エラーがスローされます。この更新を停止するには、条件を使用して、発生したエラーのタイプを確認します。if else
アボートエラーの場合は、状態を更新する必要はありません。更新しない場合は、エラーを処理します。
// Post component
import React, { useState, useEffect } from "react";
export default function Post() {
const [posts, setPosts] = useState([]);
const [error, setError] = useState(null);
useEffect(() => {
const controller = new AbortController();
const signal = controller.signal;
fetch("https://jsonplaceholder.typicode.com/posts", { signal: signal })
.then((res) => res.json())
.then((res) => setPosts(res))
.catch((err) => {
if (err.name === "AbortError") {
console.log("successfully aborted");
} else {
setError(err);
}
});
return () => controller.abort();
}, []);
return (
<div>
{!error ? (
posts.map((post) => (
<ul key={post.id}>
<li>{post.title}</li>
</ul>
))
) : (
<p>{error}</p>
)}
</div>
);
}
フェッチを使用する場合とAxiosを使用する場合のメソッドのみを使用する必要があることに注意してください。err.name === "AbortError"axios.isCancel()
これで完了です。
useEffect
上記の例のように、クリーンアップを必要としないものとクリーンアップを必要とするものの2種類の副作用があります。useEffect
フックのクリーンアップ機能をいつどのように使用してメモリリークを防ぎ、アプリケーションを最適化するかを学ぶことは非常に重要です。
この記事がお役に立てば幸いです。クリーンアップ機能を適切に使用できるようになりました。
リンク: https://blog.logrocket.com/understanding-react-useeffect-cleanup-function/
1640668102
ReactのuseEffect
クリーンアップ機能は、エフェクトをクリーンアップすることにより、メモリリークなどの望ましくない動作からアプリケーションを保護します。そうすることで、アプリケーションのパフォーマンスを最適化できます。
この記事を始めるにuseEffect
は、APIをフェッチするために使用することを含め、何であるかについての基本的な理解が必要です。この記事では、useEffect
フックのクリーンアップ機能について説明します。この記事の終わりまでに、クリーンアップ機能を快適に使用できるようになることを願っています。
useEffect
クリーンアップ機能とは何ですか?名前が示すように、useEffect
クリーンアップはフックの関数であり、コンポーネントがアンマウントされる前にコードを整理することができます。コードが実行され、レンダリングごとに再実行されると、クリーンアップ関数を使用してコード自体もクリーンアップされます。useEffect
useEffect
useEffect
フックは、我々はその中の関数を返すことができますし、クリーンアップが起こるところ、このリターン機能があるような方法で構築されています。クリーンアップ機能は、メモリリークを防ぎ、不要で不要な動作を削除します。
return関数内の状態も更新しないことに注意してください。
useEffect(() => {
effect
return () => {
cleanup
}
}, [input])
useEffect
クリーンアップ機能が役立つのはなぜですか?前述のように、useEffect
クリーンアップ機能は、開発者が不要な動作を防ぎ、アプリケーションのパフォーマンスを最適化する効果をクリーンアップするのに役立ちます。
ただし、useEffect
クリーンアップ関数は、コンポーネントのマウントを解除するときに実行されるだけでなく、次にスケジュールされたエフェクトの実行の直前にも実行されることに注意してください。
実際、エフェクトが実行された後、次にスケジュールされるエフェクトは通常、:に基づいています。dependency(array)
// The dependency is an array
useEffect( callback, dependency )
したがって、効果がプロップに依存している場合、または持続するものを設定する場合はいつでも、クリーンアップ関数を呼び出す理由があります。
このシナリオを見てみましょう。ユーザーのを介して特定のユーザーid
のフェッチを取得し、フェッチが完了する前に、考えを変えて別のユーザーを取得しようとするとします。この時点で、id
前のフェッチ要求がまだ進行中の間に、小道具、この場合は、が更新されます。
次に、クリーンアップ関数を使用してフェッチを中止し、アプリケーションがメモリリークにさらされないようにする必要があります。
useEffect
クリーンアップはいつ使用する必要がありますか?データをフェッチしてレンダリングするReactコンポーネントがあるとしましょう。promiseが解決する前にコンポーネントがアンマウントされると、useEffect
(マウントされていないコンポーネントの)状態を更新しようとし、次のようなエラーを送信します。
このエラーを修正するには、クリーンアップ機能を使用してエラーを解決します。
Reactの公式ドキュメントによると、「Reactは、コンポーネントがアンマウントされたときにクリーンアップを実行します。ただし…エフェクトは、1回だけでなく、すべてのレンダリングに対して実行されます。これが、Reactが次回エフェクトを実行する前に、前のレンダリングのエフェクトもクリーンアップする理由です。」
クリーンアップは通常、行われたすべてのサブスクリプションをキャンセルし、フェッチ要求をキャンセルするために使用されます。それでは、コードを書いて、これらのキャンセルをどのように実行できるかを見てみましょう。
サブスクリプションのクリーンアップを開始するには、最初にサブスクライブを解除する必要があります。これは、アプリをメモリリークにさらしたくないため、アプリを最適化するためです。
私たちのコンポーネントアンマウント前に、当社のサブスクリプションから退会するには、私たちの変数を設定し、聞かせてisApiSubscribed
、とtrue
し、我々はそれを設定することができfalse
、我々はアンマウントしたいとき:
useEffect(() => {
// set our variable to true
let isApiSubscribed = true;
axios.get(API).then((response) => {
if (isApiSubscribed) {
// handle success
}
});
return () => {
// cancel the subscription
isApiSubscribed = false;
};
}, []);
上記のコードでは、変数を設定isApiSubscribed
しtrue
た後、私たちの成功の要求を処理するための条件としてそれを使用します。ただし、コンポーネントをアンマウントisApiSubscribed
するfalse
ときに変数をに設定します。
フェッチリクエストの呼び出しをキャンセルするには、さまざまな方法があります。AxiosのキャンセルトークンAbortController
を使用するか、使用します。
を使用するAbortController
には、コンストラクターを使用してコントローラーを作成する必要があります。次に、フェッチリクエストが開始されると、リクエストのオブジェクト内にオプションとして渡されます。AbortController()AbortSignaloption
これにより、コントローラーとシグナルがフェッチリクエストに関連付けられ、次を使用していつでもキャンセルできます。AbortController.abort()
>useEffect(() => {
const controller = new AbortController();
const signal = controller.signal;
fetch(API, {
signal: signal
})
.then((response) => response.json())
.then((response) => {
// handle success
});
return () => {
// cancel the request before component unmounts
controller.abort();
};
}, []);
さらに進んで、catchにエラー条件を追加して、中止したときにフェッチ要求がエラーをスローしないようにすることができます。このエラーが発生するのは、アンマウント中に、エラーを処理するときに状態を更新しようとするためです。
私たちにできることは、条件を記述して、どのようなエラーが発生するかを知ることです。アボートエラーが発生した場合、状態を更新したくありません。
useEffect(() => {
const controller = new AbortController();
const signal = controller.signal;
fetch(API, {
signal: signal
})
.then((response) => response.json())
.then((response) => {
// handle success
console.log(response);
})
.catch((err) => {
if (err.name === 'AbortError') {
console.log('successfully aborted');
} else {
// handle error
}
});
return () => {
// cancel the request before component unmounts
controller.abort();
};
}, []);
これで、リクエストが解決する前に焦って別のページに移動した場合でも、コンポーネントがアンマウントされる前にリクエストが中止されるため、そのエラーが再び発生することはありません。アボートエラーが発生した場合、状態も更新されません。
それでは、AxiosのキャンセルオプションであるAxiosキャンセルトークンを使用して同じことを行う方法を見てみましょう。
まず、from Axiosをsourceという名前の定数に格納し、トークンをAxiosオプションとして渡し、次のコマンドでいつでもリクエストをキャンセルします。CancelToken.source()source.cancel()
useEffect(() => {
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
axios
.get(API, {
cancelToken: source.token
})
.catch((err) => {
if (axios.isCancel(err)) {
console.log('successfully aborted');
} else {
// handle error
}
});
return () => {
// cancel the request before component unmounts
source.cancel();
};
}, []);
AbortError
inAbortController
で行ったのと同じように、AxiosはisCancel
、エラーの原因を確認し、エラーの処理方法を知ることができるというメソッドを提供します。
Axiosソースが中止またはキャンセルされたためにリクエストが失敗した場合、状態を更新する必要はありません。
useEffect
クリーンアップ機能の使い方上記のエラーが発生する可能性がある場合の例と、発生した場合のクリーンアップ関数の使用方法を見てみましょう。2つのファイルを作成することから始めましょう:Post
とApp
。次のコードを記述して続行します。
// Post component
import React, { useState, useEffect } from "react";
export default function Post() {
const [posts, setPosts] = useState([]);
const [error, setError] = useState(null);
useEffect(() => {
const controller = new AbortController();
const signal = controller.signal;
fetch("https://jsonplaceholder.typicode.com/posts", { signal: signal })
.then((res) => res.json())
.then((res) => setPosts(res))
.catch((err) => setError(err));
}, []);
return (
<div>
{!error ? (
posts.map((post) => (
<ul key={post.id}>
<li>{post.title}</li>
</ul>
))
) : (
<p>{error}</p>
)}
</div>
);
}
これは、すべてのレンダリングで投稿を取得し、フェッチエラーを処理する単純な投稿コンポーネントです。
ここでは、メインコンポーネントに投稿コンポーネントをインポートし、ボタンをクリックするたびに投稿を表示します。ボタンは投稿を表示および非表示にします。つまり、投稿コンポーネントをマウントおよびアンマウントします。
// App component
import React, { useState } from "react";
import Post from "./Post";
const App = () => {
const [show, setShow] = useState(false);
const showPost = () => {
// toggles posts onclick of button
setShow(!show);
};
return (
<div>
<button onClick={showPost}>Show Posts</button>
{show && <Post />}
</div>
);
};
export default App;
ここで、ボタンをクリックし、投稿がレンダリングされる前にもう一度ボタンをクリックすると(別のシナリオでは、投稿がレンダリングされる前に別のページに移動する場合があります)、コンソールでエラーが発生します。
これは、ReactuseEffect
がまだ実行中であり、バックグラウンドでAPIをフェッチしようとしているためです。APIのフェッチが完了すると、状態を更新しようとしますが、今回はマウントされていないコンポーネントであるため、次のエラーがスローされます。
ここで、このエラーをクリアしてメモリリークを停止するには、上記のソリューションのいずれかを使用してクリーンアップ機能を実装する必要があります。この投稿では、以下を使用しますAbortController
:
// Post component
import React, { useState, useEffect } from "react";
export default function Post() {
const [posts, setPosts] = useState([]);
const [error, setError] = useState(null);
useEffect(() => {
const controller = new AbortController();
const signal = controller.signal;
fetch("https://jsonplaceholder.typicode.com/posts", { signal: signal })
.then((res) => res.json())
.then((res) => setPosts(res))
.catch((err) => {
setError(err);
});
return () => controller.abort();
}, []);
return (
<div>
{!error ? (
posts.map((post) => (
<ul key={post.id}>
<li>{post.title}</li>
</ul>
))
) : (
<p>{error}</p>
)}
</div>
);
}
クリーンアップ関数でシグナルを中止した後でも、アンマウントによってエラーがスローされることがコンソールに表示されます。前に説明したように、このエラーはフェッチ呼び出しを中止したときに発生します。
useEffect
catchブロックでフェッチエラーをキャッチしてから、エラー状態を更新しようとすると、エラーがスローされます。この更新を停止するには、条件を使用して、発生したエラーのタイプを確認します。if else
アボートエラーの場合は、状態を更新する必要はありません。更新しない場合は、エラーを処理します。
// Post component
import React, { useState, useEffect } from "react";
export default function Post() {
const [posts, setPosts] = useState([]);
const [error, setError] = useState(null);
useEffect(() => {
const controller = new AbortController();
const signal = controller.signal;
fetch("https://jsonplaceholder.typicode.com/posts", { signal: signal })
.then((res) => res.json())
.then((res) => setPosts(res))
.catch((err) => {
if (err.name === "AbortError") {
console.log("successfully aborted");
} else {
setError(err);
}
});
return () => controller.abort();
}, []);
return (
<div>
{!error ? (
posts.map((post) => (
<ul key={post.id}>
<li>{post.title}</li>
</ul>
))
) : (
<p>{error}</p>
)}
</div>
);
}
フェッチを使用する場合とAxiosを使用する場合のメソッドのみを使用する必要があることに注意してください。err.name === "AbortError"axios.isCancel()
これで完了です。
useEffect
上記の例のように、クリーンアップを必要としないものとクリーンアップを必要とするものの2種類の副作用があります。useEffect
フックのクリーンアップ機能をいつどのように使用してメモリリークを防ぎ、アプリケーションを最適化するかを学ぶことは非常に重要です。
この記事がお役に立てば幸いです。クリーンアップ機能を適切に使用できるようになりました。
リンク: https://blog.logrocket.com/understanding-react-useeffect-cleanup-function/