react hooks 基础知识

56 阅读3分钟

1、hooks是用来做什么的?

对函数组件进行增强,让函数型组件可以存储状态,可以拥有处理副作用的能力。 副作用:除了直接展示dom视图外的其他函数都是

2、类组件的不足

  • 缺失逻辑复用机制,为了复用逻辑增加无实际渲染效果的组件,增加了组件层级 显示十分臃肿,增加了调试的难度以及运行效率的 降低
  • 类组件经常会变得很复杂难以维护,将一组相干的业务逻辑拆分到了多个生命周期函数中,在一个生命周期函数内存在多个不相干的业务逻辑
  • 类成员方法不能保证this指向的正确性

3、useState

参数可以是一个函数,函数返回什么,初始状态就是什么,函数只调用一次,用在初始值是动态值的情况

const[count, setCount]=useState(()=>(props.count || 0))

设置状态值方法的参数可以是一个值也可以是一个函数,设置方法本身是异步的

4、useReducer

一种让函数组件保存状态的方式

  function reducer(state, action) {
    switch (action.type) {
      case 'increment':
        return state + 1
      case 'decrement':
        return state - 1
      default:
        return state
    }
  }
  const [count, dispatch] = useReducer(reducer, 0)

5、useContext

在跨组件层级获取数据时简化获取数据的代码

  import React, { createContext, useContext } from 'react'
  const countContext = createContext()

  function App() {
    return <countContext.Provider value={100}>
      <Foo />
    </countContext.Provider>
  }

  function Foo() {
    const value = useContext(contextValue)
    return <div>{value}</div>
  

6、useEffect

  • 让函数型组件拥有处理副作用的能力,类似生命周期函数
  • 结合异步函数
  • useEffect中的参数函数不能是异步函数,因为useEffect函数要返回清理资源的函数,如果是异步函数就变成 了promise
useEffect(() => {
  (async () => {
    await axios.get();
  })();
}, []);

7、useMemo

  • 类似于Vue中的计算属性,可以监测某个值的变化,根据变化值计算新值
  • useMemo会缓存计算结果,如果监测没有变化,即使组件重新渲染,也不会重新计算,此行为有助于避免在每 个渲染上进行昂贵的计算
let result = useMemo(() => {
  return count; // 操作
}, [count]);

8、memo

性能优化,如果本组件中的数据没有发生变化,阻止组件更新,类似类组件的PureComponent和 shouldComponentUpdate

const Counter = () => {
  return <div></div>;
};
export default memo(Counter);

9、useCallback

性能优化,缓存函数,是组件重新渲染时得到相同的函数实例

const resetCount = useCallback(() => setCount(count + 1), [setCount]);

10、useRef

获取dom元素

function App() {
  const box = useRef(); // { current: div }
  return (
    <div ref={box}>
      <Button onClick={() => console.log(box)}></Button>
    </div>
  );
}
  • 保存数据(跨组件周期)
  • 即使组件重新渲染,保存的数据仍然存在,保存的数据被更改不会触发组件重新渲染
function App() {
  const [count, setCount] = useState(0);
  let timerId = useRef();

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

  const stopCount = () => {
    clearInterval(timerId.current);
  };
  return (
    <div>
      {count}
      <Button onClick={stopCount}>停止</Button>
    </div>
  );
}

11、自定义hook

  • 标准的封装和共享逻辑的方式
  • 是一个函数,以use开头
  • 逻辑和内置hook的组合

12、路由hooks

import { useHistory, useLocation, useRouterMatch, useParams } from 'react-router-dom';

13、useState实现原理

let state = [];
let setters = [];
let stateIndex = 0;

function createSetter(index) {
  return function (newState) {
    state[index] = newState;
    render();
  };
}
function useState(initialState) {
  state[stateIndex] = state[stateIndex] ? state[stateIndex] : initialState;
  setters.push(createSetter(stateIndex));
  stateIndex++;
  let value = state[stateIndex];
  let setter = setters[stateIndex];
  return [value, setter];
}

为什么useState不能放在条件语句?

  • 因为react通过单链表来管理hooks

image.png update阶段,每次调用useState,链表就会执行next向后移动一步,如果将useState放在条件语句中,假设条件判断不成立,没有执行里面的useState方法,会导致接下来所有的useState的取值出现偏移,从而导致异常发生。

参考链接: juejin.cn/post/684490…

14、useEffect实现原理

let preDepsAry = [];
let effectIndex = 0;

function useEffect(callback, depsAry) {
  if (Object.prototype.toString.call(callback) !== '[object Function]')
    throw new Error('useEffect函数的第一个参数必须是函数');

  if (typeof depsAry === 'undefined') {
    callback();
  } else {
    if (Object.prototype.toString.call(depsAry) !== '[object Array]')
      throw new Error('depsAry不是一个数组');
    //判断值是否有变化
    let prevDeps = preDepsAry[effectIndex];
    let hasChanged = prevDeps
      ? depsAry.every((dep, index) => dep === preDepsAry[index]) === false
      : true;
    if (hasChanged) {
      callback();
    }
    //同步依赖值
    preDepsAry[effectIndex] = depsAry;
    effectIndex++;
  }
}

15、useReducer实现原理

function useReducer(reducer, initialState) {
  const [state, setState] = useState(initialState);
  function dispatch(action) {
    const newState = reducer(state, action);
    setState(newState);
  }
  return [state, dispatch];
}

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return state + 1;
    case 'decrement':
      return state - 1;
    default:
      return state;
  }
}

const [count, dispatch] = useReducer(reducer, 0);