最易懂-react18入门系列三

70 阅读5分钟

摘要

本篇主要是react内置常用hooks。

  • useEffect

    • 基础使用
    • 不同依赖场景
    • 清除副作用
  • 自定义hook

  • hooks使用规则说明

useEffect

useEffect是react的一个hooks函数,用于在react组件中创建不是由事件引起的,而是由渲染本身引发的动作。比如:页面首次渲染的数据获取,更改DOM等等。

语法:

useEffect(fn,[])


useEffect(()=>{},[])

是函数,那么就得调用,里面呢,有2个参数,第一个是一个fn函数,叫副作用函数,里面写的就是具体要执行的操作。第二个参数是一个数组,可选的,存放的是依赖项,也就是依赖项发生变化了,我就执行一次fn操作。

第二个参数,是可选的,当我们不传东西,是一个空数组的时候,则fn只是会在组件渲染完毕后执行一次。

基础使用

最常用场景:空数组依赖项

页面渲染完毕后获取接口数据:

import {useEffect,useState} from 'react'

const url = 'xxx'

function App(){

	// 创建状态数据
	const [list,setList]=useState([])

	useEffect(()=>{
		// 定义
		async function getList(){
			const result = await fetch(url)

			const resToJson = await result.json()

			setList(resToJson.data)
		}

		// 调用
		getList()

	},[])

	return(
		<div>
			<ul>
				{list.map(item => <li key={item.id}>{item.name}</li>)}
			</ul>
		</div>
	)
}

不同依赖场景

传入的依赖项不同,fn执行的时机也存在多种情况

依赖项副作用函数执行时机
没有依赖项组件初始渲染 + 组件更新时 执行
空数组依赖项只在组件初始渲染时执行一次
添加特定依赖项组件初始渲染 + 依赖项发生变化时 执行

依次演示:

import {useEffect,useState} from 'react'

const url = 'xxx'

function App(){

	// 创建状态数据
	const [conut,setCount]=useState(0)

	//1. 没有依赖项。初始渲染完 + 每次组件更新 时执行
	useEffect(()=>{
		console.log('fn副作用函数执行了!')
	})


	//2. 空数组依赖项。初始渲染完 执行
	useEffect(()=>{
		console.log('fn副作用函数执行了!')
	},[])


	//3. 有特定依赖项。初始渲染完 + 依赖项变化 时执行
	useEffect(()=>{
		console.log('fn副作用函数执行了!')
	},[count])


	return(
		<div>
			<button onClick={()=>setCount(count + 1)}>累加触发视图更新</button>
		</div>
	)
}

清除副作用

副作用 等价说法:对接组件外部的操作。

概念:

在useEffect中编写的由 渲染本身引起的对接组件外部的操作,社区也经常把它叫做副作用操作,比如在useEffect中开启了一个定时器,在组件卸载时需要把这个定时器清理掉,这个过程就是清理副作用。

实现

useEffect 的 fn 里 return一个函数,里面写清除副作用逻辑。

useEffect(()=>{
	//...你的操作逻辑

	return ()=>{
		// return一个函数,里面写清除副作用逻辑
	}
},[])

这样写,就是会在组件卸载时自动执行清除副作用逻辑!这个时机也是最最常用的。

案例

在Son组件渲染时开启一个定时器,卸载时清除定时器。

import {useEffect,useState} from 'react'

function Son(){

	//1. 渲染完毕时开启一个timer
	const timer = setInterval(()=>{console.log('timering...')},1000)

	// 2. 卸载时清除定时器
	return()=>{
		clearInterval(timer)
	}

	return <div>son</div>
}

function App(){

	// 通过条件渲染模拟组件被卸载
	const [show,setShow] = useState(true)

	return(
		<div>
			{show && <Son/>}
			<button onClick={()=>setShow(false)}>卸载Son组件</button>
		<div>
	)
}

自定义hooks

概念

自定义hook 是以 use 打头的函数,通过自定义hook函数可以用来实现逻辑的封装和复用。

也就是说,hook 就是一个函数,命名上以use开头,主要是完成逻辑的封装复用!

探索路线

我们拿一个案例:点击toggle按钮实现div盒子的显示与隐藏。

不封装直接实现功能 -> 封装自定义hook实现 -> 抽象实现的通用逻辑

1.不封装直接实现功能

import {useState} from 'react'

function App(){

	//---------------可复用--------------------
	const [value ,setValue] = useState(true)
	const toggle = ()=>setValue(!value)
	//---------------可复用--------------------

	return(
		<div>
			{value && <div> div show or hide</div>}
			<button onClick={toggle}>toggle</button>
		</div>
	)
}

2.封装自定义hook实现

把可复用的逻辑抽走,定义成一个hook函数。

import {useState} from 'react'

function useToggle(){
	const [value ,setValue] = useState(true)
	const toggle = ()=>setValue(!value)

	// 这个状态 和 回调函数 需要在调用处的组件里去使用的,则return丢出去供引用。
	// 这个可以return 对象,也可以return 数组来装载。

	return{
		value,
		toggle
	}
}

然后使用处:

import {useToggle} from './useToggle'

function App(){

	// 直接使用,注意这里解构 得用对象。
	const {value ,toggle} = useToggle()

	return(
		<div>
			{value && <div> div show or hide</div>}
			<button onClick={toggle}>toggle</button>
		</div>
	)
}

3.抽象实现的通用逻辑

通过上面的演示,我们可以总结下自己封装自定义hook的通用思路

  1. 声明一个use开头的函数

  2. 在函数内书写可复用的逻辑

  3. 把组件中要用的状态值和函数return出去[以对象或者数组的方式都行]

  4. 在哪个组件中要用到这个逻辑,就执行这个use函数,解构出状态和函数进行执行使用即可。

接下来,我们再自定义一个hooks,获取数据。

function useGetData(){
	const [data,setData] = useState([])

	useEffect(()=>{
		async function getList(){
			const res = await axios.get('url:xxx')
			setData(res.data||[])
		}

		getList()

	},[])

	return {
		data,
		setData
	}
}

外部组件如何使用呢?

function App(){
	const {data,setData} = useGetData()

	// 直接使用数据,或者覆盖数据即可。...
}

ReactHooks的使用规则

  1. 不能在组件外部去使用hooks。只能在组件内部或者其他自定义hooks函数里调用!

  2. 只能在组件的顶层作用域里调用,不能在if/for/其他函数中调用,会报错。

错误一

在组件外部去使用了:

import {useToggle} from './useToggle'

//错误❎
const {value ,toggle} = useToggle()

function App(){

	//正确✅
	const {value ,toggle} = useToggle()

	return(
		<div>
			{value && <div> div show or hide</div>}
			<button onClick={toggle}>toggle</button>
		</div>
	)
}

错误二

在if语句中使用了:

import {useToggle} from './useToggle'


function App(){

	//错误❎
	if(true){
		const {value ,toggle} = useToggle()
	}

	return(
		<div>
			{value && <div> div show or hide</div>}
			<button onClick={toggle}>toggle</button>
		</div>
	)
}

会报错:

React Hook 'useToggle' is called conditionally. React Hooks must be called in the exact same order in every component render