我是ReactJS的新手,正在编写代码,以便在从DB加载数据之前,它将显示加载消息,然后在加载之后,用加载的数据呈现组件。为此,我同时使用了useState钩子和useEffect钩子。这是代码:
问题是,当我用console.log检查时,useEffect被触发两次。因此,该代码将两次查询相同的数据,应该避免。
下面是我编写的代码:
import React from 'react';
import './App.css';
import {useState,useEffect} from 'react';
import Postspreview from '../components/Postspreview'
const indexarray=[]; //The array to which the fetched data will be pushed
function Home() {
const [isLoading,setLoad]=useState(true);
useEffect(()=>{
/*
Query logic to query from DB and push to indexarray
*/
setLoad(false); // To indicate that the loading is complete
})
},[]);
if (isLoading===true){
console.log("Loading");
return <div>This is loading...</div>
}
else {
console.log("Loaded!"); //This is actually logged twice.
return (
<div>
<div className="posts_preview_columns">
{indexarray.map(indexarray=>
<Postspreview
username={indexarray.username}
idThumbnail={indexarray.profile_thumbnail}
nickname={indexarray.nickname}
postThumbnail={indexarray.photolink}
/>
)}
</div>
</div>
);
}
}
export default Home;
有人可以帮助我理解为什么两次被调用以及如何正确修复代码吗? 非常感谢!
答案 0 :(得分:10)
可能还有其他副作用导致组件重新呈现,但useEffect本身仅被调用一次。您可以使用以下代码确定看到这一点。
useEffect(()=>{
/*
Query logic
*/
console.log('i fire once');
},[]);
如果多次触发“我触发一次”日志,则说明您的问题是 两件事之一。
这一点应该很明显,您的组件在页面中出现了两次,每个组件都将挂载并运行useEffect
该组件被强制卸载并在其初始渲染时重新安装。这可能像是在树的上方发生的“关键”变化。您需要使用useEffect提升每个级别,直到仅渲染一次。那么您应该能够找到原因或重新安装。
答案 1 :(得分:2)
您很可能在启用了严格模式的开发环境中检查问题。
要验证是否是这种情况,请搜索
严格模式无法自动为您检测副作用,但它可以通过使它们更具确定性来帮助您发现它们。这是通过有意重复调用以下函数来完成的:
这里有类似的问题My React Component is rendering twice because of Strict Mode
答案 2 :(得分:1)
不确定为什么不将结果置于状态,下面是一个示例,该示例只调用一次效果,因此您必须在未发布的代码中做了一些操作,使其再次呈现:
const App = () => {
const [isLoading, setLoad] = React.useState(true)
const [data, setData] = React.useState([])
React.useEffect(() => {
console.log('in effect')
fetch('https://jsonplaceholder.typicode.com/todos')
.then(result => result.json())
.then(data => {
setLoad(false)//causes re render
setData(data)//causes re render
})
},[])
//first log in console, effect happens after render
console.log('rendering:', data.length, isLoading)
return <pre>{JSON.stringify(data, undefined, 2)}</pre>
}
//render app
ReactDOM.render(<App />, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
为防止多余的渲染,您可以在一种状态下合并数据并加载:
const useIsMounted = () => {
const isMounted = React.useRef(false);
React.useEffect(() => {
isMounted.current = true;
return () => isMounted.current = false;
}, []);
return isMounted;
};
const App = () => {
const [result, setResult] = React.useState({
loading: true,
data: []
})
const isMounted = useIsMounted();
React.useEffect(() => {
console.log('in effect')
fetch('https://jsonplaceholder.typicode.com/todos')
.then(result => result.json())
.then(data => {
//before setting state in async function you should
// alsways check if the component is still mounted or
// react will spit out warnings
isMounted.current && setResult({ loading: false, data })
})
},[isMounted])
console.log(
'rendering:',
result.data.length,
result.loading
)
return (
<pre>{JSON.stringify(result.data, undefined, 2)}</pre>
)
}
//render app
ReactDOM.render(<App />, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
答案 3 :(得分:0)
我遇到过这样的问题:
const [onChainNFTs, setOnChainNFTs] = useState([]);
会触发这个 useEffect 两次:
useEffect(() => {
console.log('do something as initial state of onChainNFTs changed'); // triggered 2 times
}, [onChainNFTs]);
我确认组件 MOUNTED ONLY ONCE 和 setOnChainNFTs 没有被多次调用 - 所以这不是问题。
我通过将 onChainNFT 的初始状态转换为 null
并进行空检查来修复它。
例如
const [onChainNFTs, setOnChainNFTs] = useState(null);
useEffect(() => {
if (onChainNFTs !== null) {
console.log('do something as initial state of onChainNFTs changed'); // triggered 1 time!
}
}, [onChainNFTs]);