Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性
React hooks
基础的 Hook
- useState
- useEffect
- useContext
额外的 Hook
- useRef
- useCallback
- useMemo
- useReducer
接下来我们通过代码一一介绍下这些 hooks
1. useState 为函数组件引入状态
// 使用数组解构来命名状态变量,例如 [something, setSomething]
const [state, setState] = useState(initialState)
- 纯函数组件没有状态,
useState()
用于为函数组件引入状态 useState
的作用:只要组件的state
或者props
发生改变,组件就会重新渲染useState
只能写组件的顶层或者方法中,不能写在条件语句或循环语句中useState
的作用就是:申明局部变量和状态,声明变量- 修改变量通过
setState
来修改
import React, { useState } from 'react'
export default function HelloWorld(): any {
const a = 3, b = 7
const array = ['Apple', 'banan', 'Peach']
const [msg, setMsg] = useState<string>('hello world')
const [count, setCount] = useState<number>(1)
const [list, setList] = useState<string[]>([])
const [arrList, setArrList] = useState<string[]>(array)
const addContent = (e: any) => {
const val = e.target.value
if (e.keyCode === 13 && val) {
setList((pre) => [...list, val]) // 将输入的每一项通过展开运算符 push到列表中
e.target.value = ''
}
}
// const changeMsg = (): any => {
// setMsg('Hello!')
// }
const add = (): void => {
if (count < 5) {
setCount(() => count + 1 )
}
}
return (
<>
<h2>count的值为:{ count }</h2>
<h3>{a > b ? '大于' : '小于'}</h3>
<button onClick={ add }>+</button>
<input type="text"
onKeyUp={ addContent }
placeholder="请输入内容" />
{/* <button onClick={changeMsg}>改变文本</button> */}
<button onClick={() => (setMsg('hello1'))}>改变文本</button>
<ul>
{
list.map((item) => (<li key={Math.random()}>{ item}</li>))
}
</ul>
<div>
{
arrList.map((item, index) => (<div key={index}>{item}</div>))
}
</div>
</>
)
}
2. useEffect 副作用
useEffect(setup, dependencies?)
setup
:处理 Effect 的函数。setup 函数选择性返回一个 清理(cleanup
) 函数- 可选
dependencies
:依赖项 - 操作DOM,发送请求都属于副作用
useEffect
有点类似于vue中的watch,可以监听某个变量的变化useEffect
是一个 Hook,因此只能在 组件的顶层 或自己的 Hook 中调用它,而不能在循环或者条件内部调用。如果需要,抽离出一个新组件并将 state 移入其中- 如果你熟悉 React class 的生命周期函数,你可以把
useEffect Hook
看做componentDidMount
,componentDidUpdate
和componentWillUnmount
这三个函数的组合
import React,{ useState, useEffect } from 'react'
export default function UseEffect() {
// useEffect接收2个参数,1.是回调函数,2是回调函数依赖的数组
// 当数组中的数据发生改变的时候,回调函数会执行
// useEffect中的第二个参数不存在的时候,那么每次组件渲染的时候,回调函数都会执行
// useEffect可以用来代替生命周期函数,套路就是"第二个参数的变化"
const [count, setC] = useState<number>(1)
const [count2, setC2] = useState<number>(1)
// 第二个参数不存在,所以每次都会执行回调函数
useEffect(() => {
console.log('每次都会执行, 等价于componentDidMount + componentDidUpdate')
})
// 第二个参数如果是空数组,那么只在初始化的时候回调函数执行一次
useEffect(() => {
console.log('只有第一次执行, 等价于componentDidMount')
}, [])
// 第二个参数有值,且当值发生改变的时候执行回调函数
useEffect(() =>{
console.log('当count变化的时候执行,等价于componentDidMount + componentWillUnmount')
}, [count])
return (
<>
<h2>我是UseEffect组件 count值为:{count}</h2>
<button onClick={()=> setC(count + 1)}>+</button>
<h2>我是组件2 count值为:{count2}</h2>
<button onClick={() => setC(count2 + 1)}>+</button>
</>
)
}
传递一个函数
要实际 存储 一个函数,你必须在两种情况下在它们之前加上 () =>
。
然后 React 将存储你传递的函数。
const [fn, setFn] = useState(() => someFunction);
function handleClick() {
setFn(() => someOtherFunction);
}
3.useContext 用来解决跨组件传递数据的问题
- 向组件树深层传递数据
- 通过
context
更新传递的数据 - 覆盖组件树一部分的
context
- 在传递对象和函数时优化重新渲染
const value = useContext(MyContext)
这里还得再提到一个React的API
createContext
使用
createContext
创建组件能够提供与读取的 上下文(context
)。
createContext(defaultValue)
SomeContext.Provider
SomeContext.Consumer
(老方法)
用法
createContext(defaultValue)
- 创建上下文
- 从一个文件导入和导出上下文
// 在任意组件外调用 createContext 创建一个上下文
import { createContext } from 'react';
const ThemeContext = createContext('light');
从一个文件导入和导出上下文
通常,来自不同文件的组件都会需要读取同一个上下文。因此,在一个单独的文件内定义上下文便成了常见做法。以使用export
语句 将其导出,以便其他文件读取使用:
// Contexts.js
import { createContext } from 'react';
export const ThemeContext = createContext('light');
export const AuthContext = createContext(null);
在其他文件中定义的组件可以使用
import
语句读取或提供该context
:
// Button.js
import { ThemeContext } from './Contexts.js';
function Button() {
const theme = useContext(ThemeContext);
// ...
}
具体使用
// 在 src目录下新建一个公共的 useContext 上下文环境
import React, { createContext, useState } from 'react'
export const myContext = createContext<any>(null)
在需要嵌套的顶层组件中引入此 useContext
import React, { useState, useContext } from 'react'
import { myContext } from './MyContext';
import Parent from './Parent';
export default function RootComponent() {
const [count, setCount] = useState<number>(0)
const [msg, setMsg] = useState<string>('To chaild')
const increase = () => {
setCount(() => count + 1)
}
return (
<div>
<h2>Root 组件</h2>
<button onClick={increase}>点击</button>
{/* 用 myContext.Provider包裹顶层组件,并在 value 属性中传递需要传递的方法或属性 */}
<myContext.Provider value={{msg, count, increase}}>
<Parent />
</myContext.Provider>
</div>
)
}
在 Parent.tsx 中,通过 useContext
使用顶层组件中的属性或方法
import React, { useContext } from 'react'
import {myContext} from './myContext';
import Child from './Child'
export default function Parent() {
// 在这里使用存储在 myContext中的数据
const context = useContext(myContext)
const login = () => {
console.log('login is on')
}
return (
<div>
<h2>Parent</h2>
<h1 style={{color: 'red'; margin: '20px 0';}}>{msg}</h1>
<button onClick={login}>Login</button>
<Child />
</div>
)
}
在 Child.tsx中,通过 useContext
将顶层组件中的数据传递给Child
import React, { useContext } from 'react'
import { myContext } from './myContext';
export default function Child() {
const context = useContext<any>(myContext)
const {count, msg, setCount, increase} = context
return (
<div>
<h2>Child</h2>
<h1 style={{color: 'blue', marginTop: '0.5rem'}}>{msg} {count} { msg }</h1>
{/*子组件触发父组件传递过来的方法即可与父组件通信 */}
<button onClick={increase}>Increase</button>
</div>
)
}