React Hooks

141 阅读6分钟

个人学习笔记

ReactHooks介绍

React Hooks是用来做什么的

  • 对函数型组件进行增强,让函数组件可以存储状态,可以拥有副作用的能力,让开发者在不使用类组件的情况下,实现相同的功能

类组件的不足(Hooks)要解决的问题

  1. 缺少逻辑复用机制,为了复用逻辑增加无实际渲染效果的组件,增加了组件层级,显示十分臃肿,增加了调试的难度以及运行效率低
  2. 类组件经常会变得很复杂难以维护,将一组相干的业务逻辑拆分到多个生命周期函数中,在一个生命周期函数内存在多个不相干的业务逻辑
  3. 类成员方法不能保证this指向的正确性
  4. Hooks 意为钩子,React Hooks 就是一堆钩子函数,React通过这个钩子函数对函数型组件进行增强,不同的钩子函数提供了不同的功能

一:useState

import React, { useState } from 'react'
   
// 第一个参数是保存的状态,第二个参数是设置状态的方法
const [count, setCount] = useState(0)

二:useState 细节

  1. 接收唯一的参数既状态的初始值,初始值可以是任意数据类型
  2. 返回值为数组,数组中存储状态值和更改状态值的方法,方法名称约定以set开头,后面接状态名称
  3. 方法可以被调用多次,用于保存不同状态值
  4. 参数可以是一个函数,函数返回什么,初始值就是什么,函数只会被调用一次,用在初始值是动态值的情况
  5. 设置状态值方法的参数可以是一个值也可以是一个函数
  6. 设置状态值方法的方法本身是异步的
import React, { useState } from 'react'
   
const [person, setPerson] = useState({
            name: '123',
            age: 18
        })
        
        // 只想设置一个
        setPerson({
            ...person,
            name: '456'
        })
import React, { useState } from 'react'
   
const [count, setCount] = useState(0)
   
// 1.可以传递一个函数
setCount(() => {
            return 100
        })
        
setCount((count) => {
            return count + 1
        })
        
// 2.方法本身是异步的
setCount((count) => {
            let cnt = count + 1
            // 执行这里是是 1
            document.title = cnt
            return cnt
        })
// 执行这里是count依然是 0
document.title = count     

三:useReducer

  1. useReducer 是另一种让函数组件保存状态的方式
  2. 值被保存到了一个特定的地方,组件想要更改状态,组件需要通过dispatch方法去触发一个action
import React, { useReducer } from 'react'
   
const App = () => {
	const reducer = (state, action) => {
		switch (action.type) {
			case 'increment':
				return state + 1
			case 'decrement':
				return state - 1
		}
	}
	// 第一个参数就是上面的方法
	// 第二个参数是 count 的值
const [count, dispatch] = useReducer(reducer, 0)
	return (
		<div>
			<button
				onClick={() => {
					dispatch({ type: 'increment' })
				}}
			>
				+1
			</button>
			<span>{count}</span>
			<button
				onClick={() => {
					dispatch({ type: 'decrement' })
				}}
			>
				-1
			</button>
		</div>
	)
}
export default App

四:useContext

  1. 在跨组件层级获取数据时简化获取数据的代码
import { createContext, useContext } from 'react'
   
const countContext = createContext()
function App() {
	return (
		<countContext.provider value={100}>
			<Foo />
		</countContext.provider>
	)
}

export default App
// 获取传递值,没有简化的版本
function Foo() {
	return (
		<countContext.Consumer>
			{(value) => {
				return <div>{value}</div>
			}}
		</countContext.Consumer>
	)
}
// 使用useContext的简化版本
function Foo() {
	const count = useContext(countContext)
	return <div>{count}</div>
}

五:useEffect钩子函数执行时机分析

  1. 让函数类型组件拥有处理副作用的能力,类似生命周期函数
  2. 可以把 useEffect 看做 componentDidMount,componentDidUpdate 和 componentWillUnmount 这个三个函数的组合
  3. useEffect(()=>{}) => componentDidMount, componentDidUpdate
  4. useEffect(()=>{},[]) => componentDidMount
  5. useEffect(()=> ()=>{}) =>componentWillUnmount
import React, { useEffect, useState } from 'react'

const App = () => {
	const [count, setCount] = useState(0)

	// 会在组件挂载完成之后执行  组件数据更新完成之后执行
	useEffect(() => {
		console.log('123')
	})

	// 只在组件挂载完成执行
	useEffect(() => {
		console.log('456')
	}, [])

	// 组件卸载之前执行
	useEffect(() => {
		return () => {
			console.log('789')
		}
	})
	return (
		<div>
			<span>{count}</span>
			<button
				onClick={() => {
					setCount(count + 1)
				}}
			>
				+1
			</button>
		</div>
	)
}

六:useEffect的使用方式

import React, { useEffect, useState } from 'react'

const App = () => {
	const onScroll = () => {
		console.log('页面发生了滚动')
	}
	useEffect(() => {
		window.addEventListener('scroll', onScroll)
		return () => {
			window.removeEventListener('scroll', onScroll)
		}
	}, [])

	const [count, setCount] = useState(0)

	useEffect(() => {
		const timerId = setInterval(() => {
			setCount((count) => count + 1)
		}, 1000)
		return () => {
			clearInterval(timerId)
		}
	}, [])

	// useEffect 的第二个参数
	// 只有指定数据发生变化时触发effect
	useEffect(() => {
		console.log('count数值发生改变')
	}, [count])

	// useEffect 钩子函数结合异步函数
	// useEffect中的参数函数不能是异步函数,因为useEffect函数要返回清理资源的函数,如果是异步函数就变成了返回Promise
	useEffect(async () => {
		let data = await a()
	}, [])

	// 使用这种方法可以是使用
	useEffect(() => {
		;(async () => {
			let a = await getData()
		})()
	}, [])

	return <div>aa</div>
}

export default App

七:useMemo

  1. useMemoe 的行为类似 Vue 中的计算属性,可以检测某个值的变化,根据变化值计算新值
  2. useMemo 会缓存计算结果,如果检测值没有发生变化,即使组件重新渲染,也不会重新计算,此行为可以有助于避免在每个渲染上进行昂贵的计算
import React, { useState, useMemo } from 'react'

const App = () => {
	const [count, setCount] = useState(0)

	const result = useMemo(() => {
		// 如果 count 值发生变化此函数重新执行,并且可以在视图上渲染
		return count * 2
	}, [count])

	return (
		<div>
			{count} {result}
		</div>
	)
}

export default App

八:memo

  1. 使用memo方法提高组件性能
  2. 如果本组件中的数据没有发生变化,阻止组件更新,类似组件中的PureComponent 和 shouldComponentUpdate
import React, { memo } from 'react'

const App = () => {
	const [count, setCount] = useState(0)

	return (
		<div>
			<div>APP</div>
			<button
				onClick={() => {
					setCount(count + 1)
				}}
			>
				+1
			</button>
			<Foo />
		</div>
	)
}

// count发生改变  Foo不需要重新渲染,减少不必要的渲染
const Foo = memo(() => {
	console.log('使用memo后,count发生变化,这个组件不会重新渲染')
	return (
		<div>
			<div>Foo</div>
		</div>
	)
})

export default App

九:useCallback

  1. 性能优化,缓存函数,使组件重新渲染时得到相同的函数实例
import React, { useState, useCallback } from 'react'

const App = () => {
	const [count, setCount] = useState(0)
	// 使用useCallback 后在Foo组件调用resetCount方法,Foo不会重新渲染
	// 没有使用则重新渲染
	const resetCount = useCallback(() => {
		setCount(0)
	}, [setCount])

	return (
		<div>
			<div>{count}</div>
			<Foo resetCount={resetCount} />
		</div>
	)
}

// count发生改变  Foo不需要重新渲染,减少不必要的渲染
const Foo = memo((props) => {
	return (
		<div>
			<button onClick={props.resetCount}>resetCount</button>
		</div>
	)
})

export default App

十:useRef

  1. 获取DOM元素
  2. 保存数据(跨组件周期)即使组件重新渲染,保存的数据仍然存在,保存的数据更改不会触发组件重新渲染
import React, { useRef, useState, useEffect } from 'react'

const APP = () => {
	const [count, setCount] = useState(0)
	const username = useRef()
	// 可以获取到一个 current { current: input }
	const handle = () => console.log(username)

	// 保存数据,组件重新渲染时,数据依旧存在,数据不会丢失
	let timerId = useRef()

	useEffect(() => {
		timerId.current = setInterval(() => {
			setCount((count) => count + 1)
		}, 1000)
	}, [])

	const stopCount = () => {
		clearInterval(timerId.current)
	}

	return (
		<div>
			<input ref={username} onChange={handler} />
			<div>{count}</div>
			<button onClick={stopCount}>停止</button>
		</div>
	)
}

export default APP

十一:自定义hookH函数

  1. 自定义 Hook 是标准的封装和共享逻辑的方式
  2. 自定义 Hook 是一个函数,其名称以 use 开头
  3. 自定义 Hook 其实就是逻辑和内置 Hook 的组合
import React, { useEffect, useState } from 'react'
import axios from 'axios'

useGetPost = () => {
	const [post, setPost] = useState
	useEffect(() => {
		axios.get('url').then((res) => setPost(res.data))
	}, [])
	return [post, setPost]
}

const APP = () => {
	const [post, setPost] = useGetPost()
	return (
		<div>
			<div>自定义hook函数</div>
		</div>
	)
}

export default APP
import React, { useState } from 'react'

useUpdateInput = (initialValue) => {
	const [value, setValue] = useState(initialValue)

	return {
		value,
		onChange: (event) => setValue(event.target.value),
	}
}

const App = () => {
	const usernameInput = useUpdateInput('')
	const usewordInput = useUpdateInput('')
	const submitForm = (event) => {
		event.perventDefault()
		console.log(usernameInput.value)
		console.log(usewordInput.value)
	}
	return (
		<form onSubmit={submitForm}>
			<input type="text" name="username" {...usernameInput} />
			<input type="password" name="password" {...usewordInput} />
			<input type="submit" />
		</form>
	)
}

export default App

十二:路由Hook

  1. react-router-dom 路由提供的钩子函数
  2. useHistory,useLocation,useRouterMatch,useParams