React使用useEffect解决setState副作用详解
作者:heheer
介绍一下API
本文主要内容:描述了setState与fetch之间产生的冲突副作用,并使用useEffect进行解决
API
,即Application Programming Interface,应用程序接口,是很多程序向开发人员提供的易于使用的抽象化的代码。
比如经常会用到的查询天气API,智能识图API,如果是直接照着复杂的代码编写,会相当不友好。而API则只需按照它们提供的规则即可简单、方便、安全地使用。
fetch()方法访问API
我们会用到一个很简单的资源API,https://swapi.dev/api/people/1
,这是一个会返回星球大战里的人物信息的API。
所以我们要做的事:1、读取API中提供的数据; 2、将获得的数据写入state。
首先我们来做第一步,这里介绍一下fetch()
fetch()
必须接受一个参数——资源的路径。无论请求成功与否,它都返回一个 Promise 对象,resolve 对应请求的Response。
一旦 Response被返回,就可以使用一些方法来定义内容的形式
所以我们可以使用以下代码完成资源API的读取,并且渲染到页面上
import React from "react" export default function App() { const [starWarsData, setStarWarsData] = React.useState({}) fetch("https://swapi.dev/api/people/1") .then(res => res.json()) .then(data => setStarWarsData(data)) return ( <div> <pre>{JSON.stringify(starWarsData, null, 2)}</pre> </div> ) }
可以看到我们似乎确实轻松地获得了资源接口所提供给我们的数据
然而当我们加上控制台的输出后,事情就变得不一样了
setState的副作用
在这个程序中,我们可以加上一句console.log
在控制台输出后天的运行情况,如下
import React from "react" export default function App() { const [starWarsData, setStarWarsData] = React.useState({}) console.log("component rendered") fetch("https://swapi.dev/api/people/1") .then(res => res.json()) .then(data => setStarWarsData(data)) return ( <div> <pre>{JSON.stringify(starWarsData, null, 2)}</pre> </div> ) }
这时再运行就能清楚地看到在控制台处显示了这个组件在一直不断地生成,重新地render
我们可以简单地分析一下原因,
- 组件每次render都会触发一次fetch,
- 然后fetch获取的数据传入setState又会重新使得组件被render一遍,
而这就形成了一个死循环,致使组件不断地生成。
使用useEffect解决这个问题
在useEffect()
出现之前,react并没有setState后停止render的方法,这就使得setState的使用需要非常谨慎,不过如今提供了useEffet()
来解决这个问题
useEffect接受两个参数,其中第二个参数是可选的
useEffect(<function>, <dependency array>)
所以让我们先来尝试一下不使用第二个参数会得到什么结果
import React from "react" export default function App() { const [starWarsData, setStarWarsData] = React.useState({}) console.log("component rendered") React.useEffect(function(){ fetch("https://swapi.dev/api/people/1") .then(res => res.json()) .then(data => setStarWarsData(data)) }) return ( <div> <pre>{JSON.stringify(starWarsData, null, 2)}</pre> </div> ) }
可以看到在上面的代码里我们已经按照语法要求使用了useEffect()
,然而结果却不如我们所设想的只打印一条语句,依旧是一个死循环
原因在于只使用一个参数的useEffect()
的效果是在组件被挂载
和被更新
两种情况下执行参数的函数,所以并不能解决更新状态不执行的效果
那么就要用到第二个参数了,第二个参数叫做dependency array
,只有在这个数组里的元素更新了,才会触发这个useEffect
所以这里我们可以将第二个参数设置为一个空数组,这样只有在组件刚刚被挂载的时候才会执行useEffect
,很好的解决了我们只需要读取一遍API的任务要求
import React from "react" export default function App() { const [starWarsData, setStarWarsData] = React.useState({}) console.log("component rendered") React.useEffect(function(){ fetch("https://swapi.dev/api/people/1") .then(res => res.json()) .then(data => setStarWarsData(data)) }, []) return ( <div> <pre>{JSON.stringify(starWarsData, null, 2)}</pre> </div> ) }
使用useEffect操控函数运行
由dependency array
我们也可以得出另一种用法,可以看以下代码
import React from "react" export default function App() { const [count, setCount] = React.useState(0) console.log("Component rendered") React.useEffect(() => { console.log("Effect function ran") }, [count]) return ( <div> <h2>The count is {count}</h2> <button onClick={() => setCount(prevCount => prevCount + 1)}>Add</button> </div> ) }
我们在第二个参数处填入了会随着我们点击而变化的count
,所以在我们每次点击使得count
增加以后,count state
发生变化,执行useEffect
第一个参数的函数
可以看到设置了count state
,再在useEffect
中设置count
为dependency
,这样每次改变count的值就会再一次执行useEffect中的函数。
以上就是React使用useEffect解决setState副作用详解的详细内容,更多关于React useEffect解决setState的资料请关注脚本之家其它相关文章!