Hook是什么
一组钩子函数的集合
功能:增强了函数组件的功能
状态
生存周期
hook不能提供所有的生命周期的功能
引用
其它功能,可以自定义hook
hook的使用
class组件 功能齐全,但使用麻烦 状态、生存周期、引用
函数组件+hook 功能齐全,而且使用方便 状态、生存周期、引用、其它
1、useState和useEffect的使用
在使用useState() Hook 时,必须遵循 Hook
的规则
仅顶层调用 Hook :不能在循环,条件,嵌套函数等中调用useState()。在多个useState()调用中,渲染之间的调用顺序必须相同。仅从React 函数调用 Hook:必须仅在函数组件或自定义钩子内部调用useState()
useState 只有简单的String,Boolean等可以直接使用 可以是一些比较简单的表达式
处理复杂状态要改用useReducer()
简单数组或对象的修改,可以使用useState
useEffect第二个参数
的使用
React Hooks只能用于函数组件,而每一次函数组件被渲染,都是一个全新的开始;
1、每一个全新的开始,所有的局部变量全都重来,全体失忆;
2、每一次全新的开始,只有Hooks函数(比如useEffect)具有上一次渲染的“记忆”;
3、对于上面说的问题,因为count每次渲染都会改变,而且我们想要useEffect总会用上count的值,所以,就要把count放在useEffect的第二个数组参数里面。
规矩就是:如果useEffect第一个函数参数直接或者间接用上某个变量,就请把这个变量放在useEffect的第二个参数里。
useEffect陷进
//setItems 更新 state 不会将旧值“合并” - 它会使用新值覆盖state。 这与this.setState在类中的工作方式不同。
setItems([
...items,
{
id: items.length,
value: Math.random() * 100
}
]);
import React ,{ useEffect ,useState} from "react"
export default function Main(){
const [count ,setCount] = useState(0)
useEffect(()=>{
setTimeout(()=>{
setCount(x => x+1)
},1000)
})
return (
<div>
<div>{count}</div>
</div>
)
}
2、useRef的使用
ref
在类组件中,ref的使用可以和vue中一样,不过该写法在react类组件中逐渐废除
类组件
//this.refs.ss.style.color = 'red' 这种写法浏览器控制台会发出警告
export default class Main extends React.Component{
constructor(props){
super(props)
}
render(){
return (
<div>
<Button onClick={ () => {
this.refs.ss.style.color = 'red'
}}>+1</Button>
<span ref="ss">sss</span>
</div>
)
}
}
函数组件
import React ,{useRef} from "react"
export default function Main(){
const ss = useRef()
return (
<div>
<Button onClick={ () => {
ss.current.style.color = 'red'
}}>+1</Button>
<span ref={ss}>sss</span>
</div>
)
}
3、useContext的使用
接收一个 context 对象(React.createContext
的返回值)并返回该 context 的当前值。
当前的 context 值由上层组件中距离当前组件最近的 <TextContext.Provider> 的 value prop 决定。
当组件上层最近的 <MyContext.Provider>
更新时,该 Hook 会触发重渲染,
并使用最新传递给 MyContext provider 的 context value 值。
import React ,{ useContext} from "react"
//TextContext 是自定义的一个常量
const TextContext = React.createContext({})
const One = ()=>{
const { name } = useContext(TextContext)
return (
<p>one:{name}</p>
)
}
const Two = ()=>{
const { name } = useContext(TextContext)
return (
<p>two:{name}</p>
)
}
export default function Main(){
return (
<div>
<TextContext.Provider value={{name:'terry'}}>
<One />
<Two />
</TextContext.Provider>
</div>
)
}
4、useCallback和useMemo的使用
useCallback和useMemo的参数跟useEffect一致,他们之间最大的区别有是useEffect会用于处理副作用,而前两个hooks不能。
useMemo和useCallback都会在组件第一次渲染的时候执行,之后会在其依赖的变量发生改变时再次执行;并且这两个hooks都返回缓存的值,useMemo返回缓存的变量
,useCallback返回 缓存的函数
。
useMemo
import React,{ useState } from "react"
export default function About(){
const [count, setCount] = useState(1);
const [val, setValue] = useState('小明');
function add() {
console.log('compute');
let sum = 0;
for (let i = 0; i < count * 10; i++) {
sum += i;
}
return sum;
}
return (
<div>
<h4>{count}---{add()}</h4>
{val}
<div>
<button onClick={() => setCount(count + 1)}>+1</button>
<input value={val} onChange={event => setValue(event.target.value)}/>
</div>
</div>
)
}
运行这个代码,我们会发现,无论我们点击按钮或者在输入框添加值,在后台都会打印出compute,
然而add函数与val并无关系,这就造成了性能的损耗
。我们要改成当我们只点击按钮时,add函数才调用。
import React ,{ useState, useMemo } from 'react'
export default function About() {
const [count, setCount] = useState(1);
const [val, setValue] = useState('小明');
//useMemo
const add = useMemo(() => {
console.log('compute');
let sum = 0;
for (let i = 0; i < count * 10; i++) {
sum += i;
}
return sum;
},[count])
return (
<div>
<h4>{count}-{add}</h4>
{val}
<div>
<button onClick={() => setCount(count + 1)}>+c1</button>
<input value={val} onChange={event => setValue(event.target.value)}/>
</div>
</div>
);
}
这是我们发现,只有当我们点击了按钮时,add函数才被调用,节省了性能的消耗。
useCallback
1、问题引发
子组件onChange调用了父组件的handleOnChange
父组件handleOnChange内部会执行setText(e.target.value)引起父组件更新
父组件更新会得到新的handleOnChange,传递给子组件,对于子组件来说接收到一个新的props
子组件进行不必要更新
2、使用useCallback解决
import React, { useState, memo, useMemo, useCallback } from 'react'
const Child = memo((props) => {
console.log(props);
return (
<div>
<input type="text" onChange={props.onChange}/>
</div>
)
})
export default function About() {
const [count, setCount] = useState(0)
const [text, setText] = useState('')
//useCallback
const handleOnChange = useCallback((e) => {
setText(e.target.value)
},[])
return (
<div>
<div>count: {count}</div>
<div>text: {text}</div>
<button onClick={() => {
setCount(count + 1)
}}>+1</button>
<Child onChange={handleOnChange} />
</div>
)
}
handleOnChange被缓存了下来,尽管父组件更新了,但是拿到的handleOnChange还是同一个
对比useMemo,useMemo缓存的是一个值,useCallback缓存的是一个函数,是对一个单独的props值进行缓存
memo缓存的是组件本身,是站在全局的角度进行优化
7、useReducer的使用
const [state, dispatch] = useReducer(reducer, initialState);
在 hooks 中提供了的 useReducer 功能,可以增强 ReducerDemo 函数提供类似 Redux 的功能,引入 useReducer 后,
useReducer 接受一个 reducer 函数作为参数,reducer 接受两个参数一个是 state 另一个是 action 。
然后返回一个状态 count 和 dispath,count 是返回状态中的值,而 dispatch 是一个可以发布事件来更新 state 的。
import React,{useReducer} from 'react'
export default function ReducerDemo() {
const [count, dispath] = useReducer((state,action)=> {
//...
}, 0);
return (
<div>
<h1 className="title">{count}</h1>
</div>
)
}
在 useReducer 传入 reducer 函数根据 action 来更新 state,如果 action 为 add 正增加 state 也就是增加 count。
在 button 中调用 dispatch 发布 add 事件,发布 add 事件后就会在 reducer 根据其类型对 state 进行对应操作,更新 state。
export default function ReducerDemo() {
const [count, dispath] = useReducer((state,action)=> {
if(action === 'add'){
return state + 1;
}
return state;
}, 0);
return (
<div>
<h1 className="title">{count}</h1>
<button className="btn is-primary"
onClick={()=> dispath('add')}
>Increment</button>
</div>
)
}
实例
import React , { useReducer } from "react"
export default function New(){
const myReducer = (state ,action) => {
switch(action.type){
case('countUp'):
return {
...state,
count: state.count + 1
}
default:
return state
}
}
const [state , dispatch ] = useReducer(myReducer ,{count : 0})
return (
<div className="contentList">
<button onClick={() => dispatch({ type: 'countUp' })}> +1 </button>
<p>Count: {state.count}</p>
</div>
)
}