ReactJS - 使用useEffect



React 提供了useEffect 用于在组件中执行副作用。一些副作用如下:

  • 从外部来源获取数据并更新渲染内容。

  • 渲染后更新DOM元素。

  • 订阅

  • 使用计时器

  • 日志记录

在基于类的组件中,这些副作用是使用生命周期组件完成的。因此,useEffect hook 是以下生命周期事件的替代方案。

  • componentDidMount - 首次渲染完成后触发。

  • componentDidUpdate - 由于属性或状态更改导致渲染更新后触发。

  • componentWillUnmount - 组件销毁期间卸载渲染内容后触发。

让我们在本节学习如何在组件中使用effect hook。

useEffect 的签名

useEffect 的签名如下:

useEffect( <update function>, <dependency> )

其中,更新函数的签名如下:

{
   // code
   return <clean up function>
}

这里:

更新函数 - 更新函数是在每个渲染阶段后执行的函数。这对应于componentDidMountcomponentDidUpdate 事件。

依赖项 - 依赖项是一个数组,其中包含函数所依赖的所有变量。指定依赖项对于优化 effect hook 至关重要。通常,更新函数在每次渲染后都会被调用。有时不需要在每次渲染时都运行更新函数。让我们考虑一下,我们正在从外部来源获取数据并在渲染阶段之后更新它,如下所示:

const [data, setDate] = useState({})
const [toggle, setToggle] = useState(false)
const [id, setID] = useState(0)
useEffect( () => {
   fetch('/data/url/', {id: id}).then( fetchedData => setData(fetchedData) )
})
// code

每当datatoggle 变量更新时,组件都会重新渲染。但是,正如您所看到的,我们不需要在每次更新toggle状态时都运行定义的effect。为了解决这个问题,我们可以传递一个空依赖项,如下所示:

const [data, setDate] = useState({})
const [toggle, setToggle] = useState(false)
const [id, setID] = useState(0)
useEffect( () => {
   fetch('/data/url/', { id: id }).then( fetchedData => setData(fetchedData) )
}, [])

上面的代码只会在第一次渲染后运行一次 effect。尽管它会修复问题,但 effect 必须在每次id更改时运行。为了实现这一点,我们可以将id作为 effect 的依赖项包含在内,如下所示:

const [data, setDate] = useState({})
const [toggle, setToggle] = useState(false)
const [id, setID] = useState(0)
useEffect( () => {
   fetch('/data/url/', { id: id }).then( fetchedData => setData(fetchedData) )
}, [id])

这将确保 effect 只会在id修改后重新运行。

清理函数 - 清理函数用于在使用订阅函数和计时器函数时进行清理工作,如下所示:

const [time, setTime] = useState(new Date())
useEffect(() => {
   let interval = setInterval(() => {
      setTime(new Date())
   }, 1000)
   return () => clearInterval(interval)
}, [time])

让我们在后面的章节中创建一个完整的应用程序来理解清理函数。

effect hook 的特性

effect hook 的一些显著特性如下:

  • React 允许在函数组件中使用多个 effect hook。这将帮助我们为每个副作用编写一个函数,并将其设置为单独的 effect。

  • 每个 hook 将按照声明的顺序运行。开发者应确保 effect 的声明顺序正确。

  • 依赖项特性可用于提高副作用的性能和正确性。

  • 清理函数可以防止内存泄漏和不必要的事件触发。

使用 effect 获取数据

在本节中,让我们创建一个应用程序,该应用程序将从外部来源获取数据并使用useEffect hook 渲染它。

首先,创建一个新的React应用程序,并使用以下命令启动它。

create-react-app myapp
cd myapp
npm start

接下来,在组件文件夹下创建一个React组件NameList(src/components/NameList.js)

function NameList() {
   return <div>names</div>
}
export default NameList

这里,NameList组件的目的是展示流行的常见名字列表。

接下来,更新根组件App.js以使用新创建的NameList组件。

import NameList from "./components/NameList";
function App() {
   return (
      <div style={{ padding: "5px"}}>
         <NameList />
      </div>
   );
}
export default App;

接下来,创建一个json文件names.json(public/json/names.json),并以json格式存储流行的名称,如下所示。

[
   {
      "id": 1,
      "name": "Liam"
   },
   {
      "id": 2,
      "name": "Olivia"
   },
   {
      "id": 3,
      "name": "Noah"
   },
   {
      "id": 4,
      "name": "Emma"
   },
   {
      "id": 5,
      "name": "Oliver"
   },
   {
      "id": 6,
      "name": "Charlotte"
   },
   {
      "id": 7,
      "name": "Elijah"
   },
   {
      "id": 8,
      "name": "Amelia"
   },
   {
      "id": 9,
      "name": "James"
   },
   {
      "id": 10,
      "name": "Ava"
   },
   {
      "id": 11,
      "name": "William"
   },
   {
      "id": 12,
      "name": "Sophia"
   },
   {
      "id": 13,
      "name": "Benjamin"
   },
   {
      "id": 14,
      "name": "Isabella"
   },
   {
      "id": 15,
      "name": "Lucas"
   },
   {
      "id": 16,
      "name": "Mia"
   },
   {
      "id": 17,
      "name": "Henry"
   },
   {
      "id": 18,
      "name": "Evelyn"
   },
   {
      "id": 19,
      "name": "Theodore"
   },
   {
      "id": 20,
      "name": "Harper"
   }
]

接下来,创建一个新的状态变量data,用于在NameList组件中存储流行的名称,如下所示:

const [data, setData] = useState([])

接下来,创建一个新的状态变量isLoading来存储加载状态,如下所示:

const [isLoading, setLoading] = useState([])

接下来,使用fetch方法从json文件获取流行的名称,并将其设置到useEffect hook内的data状态变量中。

useEffect(() => {
   setTimeout(() => {
      fetch("json/names.json")
         .then( (response) => response.json())
         .then( (json) => { console.log(json); setLoading(false); setData(json); } )
   }, 2000)
})

这里我们有:

  • 使用setTimout方法来模拟加载过程。

  • 使用fetch方法获取json文件。

  • 使用json方法解析json文件。

  • 使用setData将从json文件中解析的名称设置为data状态变量。

  • 使用setLoading设置加载状态。

接下来,使用map方法渲染名称。在获取过程中,显示加载状态。

<div>
   {isLoading && <span>loading...</span>}
   {!isLoading && data && <span>Popular names: </span>}
   {!isLoading && data && data.map((item) =>
      <span key={item.id}>{item.name} </span>
   )}
</div>

这里我们有:

  • 使用isLoading显示加载状态。

  • 使用data变量显示流行名称列表。

NameList组件的完整源代码如下:

import { useState, useEffect } from "react"
function NameList() {
   const [data, setData] = useState([])
   const [isLoading, setLoading] = useState([])
   useEffect(() => {
      setTimeout(() => {
         fetch("json/names.json")
         .then( (response) => response.json())
         .then( (json) => { console.log(json); setLoading(false); setData(json); } )
      }, 2000)
   })
   return (
      <div>
         {isLoading && <span>loading...</span>}
         {!isLoading && data && <span>Popular names: </span>}
         {!isLoading && data && data.map((item) =>
            <span key={item.id}>{item.name} </span>
         )}
      </div>
   )
}
export default NameList

接下来,打开浏览器并检查应用程序。它将显示加载状态,2秒后,它将获取json并显示流行的名称,如下所示:

Fetching Data using Effect

Fetching Data using Effect

DOM操作

useEffect hook 可用于使用DOM及其方法操作文档。它确保其中的代码只在DOM准备就绪后执行。让我们更改我们的名称列表应用程序并使用DOM操作更新页面的标题。

首先,打开NameList组件,并根据加载状态添加文档标题,如下所示:

useEffect(() => {
   if(isLoading)
      document.title = "Loading popular names..."
   else
      document.title = "Popular name list"
   setTimeout(() => {
      fetch("json/names.json")
         .then( (response) => response.json())
         .then( (json) => { console.log(json); setLoading(false); setData(json);} )
   }, 2000)
})

在这里,我们使用了DOM对象document.title来更新页面的标题。

最后,打开浏览器并检查文档标题如何通过DOM操作更新。

DOM Mutations

DOM Mutations

清理函数

useEffect 可用于在组件从页面文档卸载期间删除清理函数,例如clearIntervalremoveEventListener等。这将防止内存泄漏并提高性能。为此,我们可以创建我们自己的清理函数并将其从useEffect回调参数返回。

让我们更改我们的名称列表应用程序以使用setInterval代替setTimeout,然后使用clearInterval在卸载组件期间删除设置的回调函数。

首先,打开NameList组件并更新useEffect部分,如下所示:

useEffect(() => {
   if(isLoading)
      document.title = "Loading popular names..."
   else
      document.title = "Popular name list"
   let interval = setInterval(() => {
      setLoading(true)
      fetch("json/names.json")
         .then( (response) => response.json())
         .then( (json) => { console.log(json); setLoading(false); setData(json);} )
      }, 5000)
   return () => { clearInterval(interval) }
})

这里我们有:

  • 使用setImterval每5秒更新一次流行名称。

  • 在清理函数中使用clearInterval在卸载组件期间删除setInterval。

最后,打开浏览器并检查应用程序的行为。我们将看到数据每5秒更新一次。当组件卸载时,clearInterval将在后台调用。

总结

useEffect是函数组件的重要特性,使组件能够使用生命周期事件。它帮助函数组件提供具有可预测性和优化性能的丰富功能。

广告