英文:
useEffect calling function even if input is not changed
问题
When you click on any <Link>
in MovieBlock.js
, it triggers a redirect after a 2-second delay. However, the return
function from the useEffect
in the Home.js
component logs "cleaned" after redirecting to the desired page. This behavior is related to how the cleanup function works in React.
In React, when you define a useEffect
with a cleanup function, the cleanup function is executed when the component is unmounted or when the dependencies specified in the useEffect
array change. In your case, you have the following useEffect
in Home.js
:
useEffect(() => {
// ...
return () => {
clearTimeout(useDelayFuctionRef.current);
console.log("cleaned");
};
}, [searchTerm]);
The cleanup function is executed when searchTerm
changes. However, when you click on a <Link>
in MovieBlock.js
, it triggers a redirection but doesn't cause the searchTerm
to change. Therefore, the cleanup function is not executed in this case.
If you want to perform cleanup when you navigate away from the Home
component by clicking on a <Link>
, you can listen to route changes using useEffect
with router
from next/router
:
import { useRouter } from "next/router";
// ...
export default function Home({ movies }) {
const router = useRouter();
// ...
useEffect(() => {
const handleRouteChange = () => {
// Perform cleanup here
clearTimeout(useDelayFuctionRef.current);
console.log("cleaned");
};
router.events.on("routeChangeStart", handleRouteChange);
return () => {
router.events.off("routeChangeStart", handleRouteChange);
};
}, []);
// ...
}
This code sets up an event listener for route changes using router.events.on
and removes the listener when the component unmounts using router.events.off
. This way, you can perform cleanup when the user navigates away from the Home
component, including when they click on a <Link>
.
英文:
Home.js
export default function Home({ movies }) {
const [searchTerm, setSearchTerm] = useState("");
const [inputChanged, setInputChanged] = useState(false);
const [loading, setLoading] = useState(false);
const useDelayFuctionRef = useRef(null);
const router = useRouter();
const client = {id:'',pass:''}
useEffect(() => {
const searchQuery = () => {
router.push(`${router.pathname}/?search=${searchTerm}`);
};
if (inputChanged) {
useDelayFuctionRef.current = setTimeout(() => {
searchQuery();
setInputChanged(false);
setLoading(false);
}, 2000);
}
return () => {
clearTimeout(useDelayFuctionRef.current);
console.log("cleaned");
};
}, [searchTerm]);
return (
<div>
<div>
<input
type="text"
onChange={(e) => {
setSearchTerm(e.target.value);
setInputChanged(true);
setLoading(true);
}}
/>
</div>
<div>
{movies.map((movie) => {
return (
<MovieBlock key={movie._id} movie={movie} client={client}/>
);
})}
</div>
</div>
);
}
export async function getServerSideProps({ query }) {
const movies = await fetch(query.string);
return {
props: {
movies,
},
};
}
MovieBlock.js
export default function MovieBlock({ movie, client }) {
return (
<div>
<Link
key={movie._id}
href={`/movie/${movie.slug}`}
>
<img src={movie.vertical_image} />
</Link>
<div>
<span>
{movie.genre.map((genre) => <Link key={genre._id} href={`/genre/${genre.slug}`}>{genre.title}</Link>
}
</span>
<span>{movie.title}</span>
</div>
</div>
);
}
When I click on any <Link>
in MovieBlock.js
it waits for 2 seconds, and then redirects to the page link that was clicked, the return
function from useEffect
logs "cleaned" after redirecting to the desired page, why is this happening on redirects other than input:onChange
although having condition ? Is there something to do with cleanup function ?
答案1
得分: 1
你正在经历的与useEffect
清理函数相关的行为与React处理组件卸载和更新的方式有关。
在你的Home
组件中,你有一个带有依赖数组[searchTerm]
的useEffect
钩子。这意味着当searchTerm
更改时,效果将运行。当你在MovieBlock.js
组件中单击一个<Link>
时,它会导航到不同的页面,导致Home
组件卸载,然后在加载新页面时重新挂载。这种卸载和重新挂载会导致useEffect
清理函数在组件卸载之前运行。
以下是该过程中发生的情况:
- 你在输入框中输入文字,这会更改
searchTerm
状态,触发带有2秒延迟的useEffect
。 - 在2秒延迟结束之前,你单击了一个
<Link>
,触发了导航到不同页面的操作。 - 当前的
Home
组件卸载,因为你正在跳转到一个新页面,并且调用了useEffect
清理函数,它使用clearTimeout(useDelayFuctionRef.current);
清除了超时并记录了“cleaned”。 - 新页面加载,并且
Home
组件重新渲染,但这次使用了新的状态和属性。 - 由于组件被重新渲染,具有2秒延迟的
useEffect
再次设置,对于searchTerm
的任何进一步更改,该过程将重复。
为了避免这种行为,你可以在useEffect
钩子中添加一个依赖项,以便它仅在组件首次挂载时运行。通过提供一个空的依赖数组([]
),效果将仅运行一次,相当于类组件中的componentDidMount
生命周期方法。
以下是更新后的useEffect
:
useEffect(() => {
const searchQuery = () => {
router.push(`${router.pathname}/?search=${searchTerm}`);
};
if (inputChanged) {
useDelayFuctionRef.current = setTimeout(() => {
searchQuery();
setInputChanged(false);
setLoading(false);
}, 2000);
}
return () => {
clearTimeout(useDelayFuctionRef.current);
console.log("cleaned");
};
}, [searchTerm, inputChanged]); // 将inputChanged添加为依赖项
通过将inputChanged
包括为依赖项,useEffect
也会在inputChanged
更改时运行,但由于它是在组件内管理的状态,不会受到导航期间的卸载行为的影响。
英文:
The behavior you are experiencing with the useEffect
cleanup function is related to how React handles component unmounting and updating.
In your Home
component, you have a useEffect
hook with a dependency array containing [searchTerm]
. This means that the effect will run whenever searchTerm
changes. When you click on a <Link>
in the MovieBlock.js
component, it navigates to a different page, causing the Home
component to unmount and then remount when the new page loads. This unmounting and remounting causes the useEffect
cleanup function to run before the component is unmounted.
Here's what happens during the process:
- You type in the input, which changes the
searchTerm
state, triggering theuseEffect
with a 2-second delay. - Before the 2-second delay elapses, you click on a
<Link>
, which triggers the navigation to a different page. - The current
Home
component unmounts since you are moving to a new page, and theuseEffect
cleanup function is called, which clears the timeout usingclearTimeout(useDelayFuctionRef.current);
and logs "cleaned". - The new page loads, and the
Home
component is re-rendered, but this time with a new state and props. - Since the component is re-rendered, the
useEffect
with the 2-second delay is set up again, and the process repeats for any further changes tosearchTerm
.
To avoid this behavior, you can add a dependency to your useEffect
hook, so it only runs when the component is initially mounted. By providing an empty dependency array ([]
), the effect will only run once, equivalent to the componentDidMount
lifecycle method in class components.
Here's the updated useEffect
:
useEffect(() => {
const searchQuery = () => {
router.push(`${router.pathname}/?search=${searchTerm}`);
};
if (inputChanged) {
useDelayFuctionRef.current = setTimeout(() => {
searchQuery();
setInputChanged(false);
setLoading(false);
}, 2000);
}
return () => {
clearTimeout(useDelayFuctionRef.current);
console.log("cleaned");
};
}, [searchTerm, inputChanged]); // Add inputChanged as a dependency
By including inputChanged
as a dependency, the useEffect
will also run when inputChanged
changes, but since it is a state that is managed within the component, it won't be affected by the unmounting behavior during navigation.
答案2
得分: 0
我太蠢了,不知道ReactDevTools扩展程序已经安装在我的浏览器中 🤦🏻♂️(在StackOverflow上查找后找到了解决方案),尝试在不同的帐户选项卡上运行,它完美运行了。
英文:
I was too dumb to know that ReactDevTools extension was installed in my browser 🤦🏻♂️(got the solution on stackoverflow after some looking), tried running on differect account tab and it worked perfectly
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论