首页 > 技术文章 > react Hooks

xiaoeshuang 2020-09-27 20:32 原文

一 什么是Hooks

   Hook 是react16.8新增特性

   Hook本身就是一类特殊的函数,他们可以为你的函数型组件注入一些特殊的功能,在不用编写类的情况下实用state 和其他react 特性

二 为什么要使用react Hooks

   1、状态逻辑难以复用

   2、组成复杂难以维护

   3、类的this 指向性问题

   hooks 允许我们使用简单的特殊函数实现class 各种功能

三、举例Hooks

 

 

useState

在 React 组件中,我们经常要使用 state 来进行数据的实时响应,根据 state 的变化重新渲染组件更新视图。

因为纯函数不能有状态,在 hooks 中,useState就是一个用于为函数组件引入状态(state)的状态钩子。

const [state, setState] = useState(initialState);

useState 的唯一参数是状态初始值(initial state),它返回了一个数组,这个数组的第[0]项是当前当前的状态值,第[1]项是可以改变状态值的方法函数

延迟初始化

initialState 参数是初始渲染期间使用的状态。 在随后的渲染中,它会被忽略了。 如果初始状态是高开销的计算结果,则可以改为提供函数,该函数仅在初始渲染时执行:

function Counter({initialCount = 0}) {
  // 初始值为1
  const [count, setCount] = useState(() => initialCount + 1);
  return (
    <>
      Count: {count}
      <button onClick={() => setCount(0)}>Reset</button>
      <button onClick={() => setCount(count + 1)}>+</button>
      <button onClick={() => setCount(prevCount => prevCount - 1)}>-</button>
    </>
  );
}

函数式更新对比普通更新

如果需要使用前一时刻的 state(状态) 计算新 state(状态) ,则可以将 函数 传递给 setState 。该函数将接收先前 state 的值,并返回更新的 state 

useReducer

useReducer 可以接受三个参数,第一个参数接收一个形如(state, action) => newState 的 reducer纯函数,使用时可以通过dispatch(action)来修改相关逻辑。

第二个参数是 state 的初始值,它返回当前 state 以及发送 action 的 dispatch 函数。

你可以选择惰性地创建初始 state,为此,需要将 init 函数作为 useReducer 的第三个参数传入,这样初始 state 将被设置为 init(initialArg)。



//官方示例
function countReducer(state, action) {
  switch (action.type) {
    case 'add':
      return state + 1;
    case 'minus':
      return state - 1;
    default:
      return state;
  }
}
function initFunc(initialCount) { return initialCount + 1; }


function Counter({initialCount = 0}) {
  const [count, dispatch] = useReducer(countReducer, initialCount, initFunc);
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => { dispatch({ type: 'add' }); }} >
        点击+1
      </button>
      <button onClick={() => { dispatch({ type: 'minus' }); }} >
        点击-1
      </button>
    </div>
  );
}

  

对比useState可知,看起来我们的代码好像变得复杂了,但实际应用到复杂的项目环境中,将状态管理和代码逻辑放到一起管理,使我们的代码具有更好的可读性、可维护性和可预测性。

useEffect 用于引用副作用

useEffect(create, deps);

副作用:

对环境的改变即为副作用,如修改 document.title

但我们不一定非要把副作用放在 useEffect 里面

实际上叫做 afterRender 更好,每次render后执行

用途

一、作为 componentDidMount 使用,[ ] 作第二个参数

二、作为 componentDidUpdate 使用,可指定依赖

三、作为 componentWillUnmount 使用,通过 return

四、以上三种用途可同时存在

  

 

 特点:

如果同时存在多个 useEffect, 会按照出现次序执行

useLayoutEffect

useEffect 在浏览器渲染完成后执行

useLayoutEffect 在浏览器渲染前执行

  

function App1() {
    const [n, setN] = useState(0)
    const time = useRef(null)
    const onClick = () => {
        setN(i => i + 1)
        time.current = performance.now()
    }
    useLayoutEffect(() => {
        if (time.current) {
            console.log(performance.now() - time.current) //大概是0.7ms
        }
    })
    useEffect(() => {
        if (time.current) {
            console.log(performance.now() - time.current) //大概是2.7ms
        }
    })
    return (
        <div className="App">
            <h1>n: {n}</h1>
            <button onClick={onClick}>Click</button>
        </div>
    );
}

  

 

 

 

useMemo==》useCallback

useCallback(x => console.log(x), [m]) 等价于

useMemo( () => x => console.log(x), [m])

对比useMemo,useMemo缓存的是一个值,useCallback缓存的是一个函数,是对一个单独的props值进行缓存

举例:

  • 子组件onChange调用了父组件的handleOnChange
  • 父组件handleOnChange内部会执行setText(e.target.value)引起父组件更新
  • 父组件更新会得到新的handleOnChange,传递给子组件,对于子组件来说接收到一个新的props
  • 子组件进行不必要更新

 

  • handleOnChange被缓存了下来,尽管父组件更新了,但是拿到的handleOnChange还是同一个
  • 对比useMemo,useMemo缓存的是一个值,useCallback缓存的是一个函数,是对一个单独的props值进行缓存
  • memo缓存的是组件本身,是站在全局的角度进行优化

 



参考 :https://juejin.im/post/6877012518183632910#heading-3 

https://www.jianshu.com/p/1252be39c702

推荐阅读