2023更新:在服务器上加载数据或使用React Query
新编写的官方文档提供了一个如何使用useEffect
获取数据而不会出现竞争条件问题的示例。
如果您正在使用像Next.js或Remix这样的元框架,它们允许在服务器上获取数据并将数据作为props传递给组件,这可以消除在客户端获取数据的需要。这往往对UX和性能更好,因为它消除了传统的渲染后获取数据模型,或者在转换时预取下一页所需的数据。
如果您想要一个纯客户端解决方案,并且与Suspense配合得很好:
2021 更新:Suspense
正如 Dan Abramov 在 this GitHub Issue 中所说:
从长远来看,我们将不再鼓励使用 useEffect 模式,因为它会导致竞争条件。例如 - 在您的调用开始和结束之间,任何事情都可能发生,并且您可能已经获得了新的 props。相反,我们将推荐使用 Suspense 进行数据获取。
是的,使用钩子函数有一个类似但不完全相同的替代方法可以替换componentDidMount
,那就是useEffect
钩子函数。
其他答案并没有真正回答你关于在哪里进行API调用的问题。你可以通过使用useEffect
并将空数组或对象作为第二个参数传入来替换componentDidMount()
进行API调用。关键在于第二个参数。如果你不提供空数组或对象作为第二个参数,API调用将在每次渲染时被调用,并且它实际上变成了一个componentDidUpdate
。
如文档中所述:
传递一个空数组[]作为输入告诉React你的effect不依赖于组件的任何值,因此该effect仅在挂载时运行并在卸载时清除;它不会在更新时运行。
以下是一些需要进行API调用的场景示例:
仅在挂载时进行API调用
尝试运行下面的代码并查看结果。
function User() {
const [firstName, setFirstName] = React.useState(null);
const [lastName, setLastName] = React.useState(null);
React.useEffect(() => {
fetch('https://randomuser.me/api/')
.then(results => results.json())
.then(data => {
const {name} = data.results[0];
setFirstName(name.first);
setLastName(name.last);
});
}, []);
return (
<div>
Name: {!firstName || !lastName ? 'Loading...' : `${firstName} ${lastName}`}
</div>
);
}
ReactDOM.render(<User />, document.querySelector('#app'));
<script src="https://unpkg.com/react@16.7.0-alpha.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.development.js"></script>
<div id="app"></div>
每当某个属性/状态改变时调用API
例如,如果您正在显示一个用户的个人资料页面,其中每个页面都有一个userID状态/prop,您应该将该ID作为值传递到useEffect
的第二个参数中,以便为新用户ID重新获取数据。componentDidMount
在这里不够用,因为如果您直接从用户A转到用户B的个人资料,则组件可能不需要重新挂载。
在传统的类方式中,您可以执行以下操作:
componentDidMount() {
this.fetchData();
}
componentDidUpdate(prevProps, prevState) {
if (prevState.id !== this.state.id) {
this.fetchData();
}
}
使用钩子,代码如下:
useEffect(() => {
this.fetchData();
}, [id]);
尝试运行下面的代码并查看结果。例如将ID更改为2,以查看
useEffect
再次运行。
function Todo() {
const [todo, setTodo] = React.useState(null);
const [id, setId] = React.useState(1);
React.useEffect(() => {
if (id == null || id === '') {
return;
}
fetch(`https://jsonplaceholder.typicode.com/todos/${id}`)
.then(results => results.json())
.then(data => {
setTodo(data);
});
}, [id]);
return (
<div>
<input value={id} onChange={e => setId(e.target.value)}/>
<br/>
<pre>{JSON.stringify(todo, null, 2)}</pre>
</div>
);
}
ReactDOM.render(<Todo />, document.querySelector('#app'));
<script src="https://unpkg.com/react@16.8.1/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.8.1/umd/react-dom.development.js"></script>
<div id="app"></div>
你应该阅读关于
useEffect
的相关内容,以便了解它的使用限制和注意事项。
useEffect()
中。 - Aaron Beall