logoSpectreAlan's blogs
React hooks
标签:
js
React
Hooks
类别:web前端
创建时间: 2020-04-18 13:56:20
字数总计: 6.57 k
建议阅读时长: 7 分钟
阅读量: 707

特性

  • 多个状态不会产生嵌套,写法还是平铺的
  • 允许函数组件使用 state 和部分生命周期
  • 更容易将组件的 UI 与状态分离

Hooks

useState

useState 接收一个函数,返回一个数组。

const [state,setState]=useState({...initialState})
  • 返回的state为最新状态,setState为更新函数
  • setState的更新为直接替换而非合并
  • 修改状态值,视图也会同时更新

vs class state

function state 的粒度更细,function state 保存的是快照,class state 保存的是最新值。 引用类型的情况下,class state 不需要传入新的引用,而 function state 必须保证是个新的引用。

useEffect

1useEffect(() => { 2 fn() 3 return unfn() 4},deps)
  • useEffect 就是监听每当依赖变化时,执行回调函数的存在函数组件中的钩子函数
  • effect在render后按照前后顺序执行
  • 可以把 useEffect Hook 看做 componentDidMount,componentDidUpdate 和 componentWillUnmount 这三个函数的组合
  • fn()组件挂载时执行,unfn用于卸载时的清除操作防止频繁操作时
  • 每次render都是独立的,里面有独立的state、effects、function积累消耗性能可能有时候内部的内容并没有发生变化,这时就会产生冗余的render,这时候就需要引入依赖,当这些参数发生变化的时候才去执行我里面的函数
  • deps Effect 的依赖项,发生变化,useEffect()就会执行
  • deps为[],代表只运行一次的 effect(仅在组件挂载和卸载时执行
  • deps不传,render后每次都按照顺序执行

uesRef

  • useRef 是一个只能用于函数组件的方法。
  • useRef 是除字符串 ref、函数 ref、createRef 之外的第四种获取 ref 的方法。
  • useRef 在渲染周期内永远不会变,因此可以用来引用某些数据。
  • 修改 ref.current 不会引发组件重新渲染。 举个🌰
1function Counter() { 2 const [count, setCount] = useState(0) 3 4 useEffect(() => { 5 const interval = setInterval(() => { 6 setCount(count + 1) 7 }, 1000) 8 return () => clearInterval(interval) 9 }, []) 10 11 return <h1>{count}</h1> 12}

此时页面的count始终为1,因为useEffect 里面使用到的state的值, 固定在了useEffect内部, 不会被改变,除非useEffect刷新,重新固定state的值,解决:

  • setCount 也可以接受一个 Function

将setCount(count + 1)改为setCount(val => val + 1),即setState(prevState => newState)

  • 删除useEffect的第二个参数(ps:该effect 现在每次重新 render 都再执行一次,产生新的闭包,引用到最新的 count。但这个方法能生效是因为触发重新 render 的动作恰巧只有 setCount,当出现多个触发 render 的动作时,会产生更多【奇怪】的结果)
  • 采用uesRef
1const [count, setCount] = useState(0); 2const countRef = useRef(count); 3 4useEffect(() => { 5 // 及时更新 count 值 6 countRef.current = count; 7}) 8 9useEffect(() => { 10 const interval = setInterval(() => { 11 // 不直接读取 count,而是 countRef.current 12 console.log('interval val:', countRef.current) 13 setCount(val => val + 1) 14 }, 1000); 15 return () => clearInterval(interval) 16}, [])

useMemo

useMemo 的用法类似 useEffect,常常用于缓存一些复杂计算的结果。useMemo 接收一个函数和依赖数组,当数组中依赖项变化的时候,这个函数就会执行,返回新的值。

1const fun = useMemo(() => { 2 // 一系列计算 3}, [deps])

举个🌰

1// Parent 2const Parent = ()= { 3 const [count, setCount] = useState(0); 4 const [name, setName] = useState('default name'); 5 console.log('Parent render:', count) 6 const data = useMemo(()=>{ 7 return {name} 8 },[name]) 9 10useEffect(() => { 11 const interval = setInterval(() => { 12 setCount(val => val + 1); 13 }, 1000); 14 return () => clearInterval(interval); 15}); 16 return ( 17 <> 18 <h1>Parent: {count}</h1> 19 <Child data={data}/> 20 </> 21 ) 22 23}
1// Child缓存方式一memo 2import React,{memo} from 'react' 3 4const Child = memo((props) =>{ 5 const {data} = props 6 console.log('child render...') 7 return ( 8 <div> 9 <div>child</div> 10 <div>{data.name}</div> 11 </div> 12 ) 13})
1// Child缓存方式二useMemo 2import React,{useMemo} from 'react' 3 4const Child = (props) =>{ 5 const {data} = props 6 return useMemo(()=>{ 7 console.log('child render...') 8 return ( 9 <div> 10 <div>child</div> 11 <div>{data.name}</div> 12 </div> 13 ) 14 },[data.name]) 15}

useCallback

useCallback跟useMemo比较类似,但它返回的是缓存的函数

1import React, { useState, useCallback, useEffect } from 'react' 2function Parent() { 3 const [count, setCount] = useState(1) 4 const [val, setVal] = useState('') 5 6 const callback = useCallback(() => { 7 return count 8 }, [count]) 9 10 return <div> 11 <h4>{count}</h4> 12 <Child callback={callback}/> 13 <div> 14 <button onClick={() => setCount(count + 1)}>+</button> 15 <input value={val} onChange={event => setVal(event.target.value)}/> 16 </div> 17 </div> 18} 19 20function Child({ callback }) { 21 const [count, setCount] = useState(() => callback()) 22 useEffect(() => { 23 setCount(callback()); 24 }, [callback]); 25 return <div> 26 {count} 27 </div> 28}

useContext

1// Context 为 context 对象(React.createContext 的返回值) 2// useContext 返回Context的返回值。 3// 当前的 context 值由上层组件中距离当前组件最近的<Context.Provider> 的 value prop 决定 4import React, { useState ,,useContext, createContext} from 'react'; 5// 创建一个 context 6const Context = createContext(0) 7 8// 使用useContext 9function Item () { 10 const count = useContext(Context) 11 return ( 12 <div>{ count }</div> 13 ) 14} 15 16function App () { 17 const [ count, setCount ] = useState(0) 18 return ( 19 <div> 20 点击次数: { count } 21 <button onClick={() => { setCount(count + 1)}}>点我</button> 22 <Context.Provider value={count}> 23 <Item/> 24 </Context.Provider> 25 </div> 26 ) 27} 28 29export default App

useReducer

概述

1const [state, dispatch] = useReducer(reducer, initialState); 2 3const initialState = {count: 0}; 4 5function reducer(state, action) { 6 switch (action.type) { 7 case 'increment': 8 return {count: state.count + 1}; 9 case ‘decrement’: 10 return {count: state.count - 1}; 11 default: 12 throw new Error(); 13 } 14} 15function Counter() { 16 const [state, dispatch] = useReducer(reducer, initialState); 17 return ( 18 <> 19 Count: {state.count} 20 <button onClick={() => dispatch({type: 'decrement'})}>-</button> 21 <button onClick={() => dispatch({type: 'increment'})}>+</button> 22 </> 23 ); 24}

useState 的替代方案,它接收一个形如 (state, action) => newState 的 reducer,并返回当前的 state 以及与其配套的 dispatch 方法

useReducer模拟redux

  • reducer
1// reducer.js 2export const reducer = (state, action) => { 3 switch(action.type) { 4 case "increment": 5 return state + 1; 6 case "decrement": 7 return state - 1; 8 default: 9 return state; 10 } 11} 12export const defaultState = 0;
  • Context
1// Context.js 2export const Context = createContext(null);
  • App.js
1App.js 2function App() { 3 const [state, dispatch] = useReducer(reducer, defaultState) 4 return ( 5 <Context.Provider value={{state, dispatch}}> 6 <ChildOne /> 7 <Chi1dTwo /> 8 </Context.Provider> 9 ) 10} 11function ChildOne { 12 const { state, dispatch > = useContext(Context); 13 return ( 14 <div> 15 <hl>{state}</hl> 16 <button onClick={() => dispatch({type: 'increment'})}> 17 increment 18 </button> 19 <button onClick={() => dispatch({ type: 'decrement'})} 20 decrement 21 </button> 22 </div> 23 ) 24}
吐槽一下
copyright