浅析React Hook

215 阅读3分钟

React Hook

React Hook 有以下几个

  1. useState (状态)
  2. useEffect / useLayoutEffect (副作用)
  3. useContent (上下文)
  4. useReducer (可代替Redux)
  5. useMemo / useCallBack (记忆)
  6. useRef / useImperativeHandle (引用)

useState

用法 :

  const [n,setN] = React.useState(0)
  const [n,setN] = React.useState( {name : 'hht'} )

上面的代码在我们每次render时都会计算一边 初始值,我们可以将其写成一个函数,来提高运行效率

  const [n, setN] = React.useState( () => 0 )

当我们在使用setN时,实际上我每次都会创建出一个新的n,因为每次setN时,都会产生一个新的作用域。

所以,为了以免将新旧n搞混,我们在使用setN时,更推荐写成函数的形式

  const add = () => {
    setN( i => i + 1 )
  }

useReducer

useReducer其实和useState类似,但是我们可以将复杂的useState写成一个useReducer

useReducer的用法:

  1. 初始化一个变量
  2. 创建所有操作
  3. 在组件中引入读写接口
  4. 调用写接口
const inital = {n: 0, i: 1}

const reducer = (state, action) => {
  if (action.type === 'add') {
    return {...inital, n: action.n}
  } else if (action.type === 'multi') {
    return {...inital, n: action.n}
  }
}

const App = () => {
  const [state, dispatch] = React.useReducer(reducer, inital)
  const add = () => {
    dispatch({type: 'add', n: state.n + 1})
  }
  return (
    <div>
      number: n : {state.n} , i : {state.i}
      <button onClick={add}>n+1</button>
    </div>
  )
}

useContext

通过useContext 可以创建一个局部的全局作用域

在useContext中的组件,可以通过useContext 中的value 任意调用 (vale最好写成对象形式)

const c = React.createContext(null)

const App = () => {
  const [n,setN] = React.useState(0)
  const add = () => {
    setN( i => i +1)
  }
  return (
    <c.Provider value={ {n,add} }>
      <Child />
    </c.Provider>
  )
}

const Child = () => {
  const {n,add} = React.useContext(c)
  return (
    <div>
      n : {n}
      <button onClick={add}>+1</button>
    </div>
  )
}

useEffect / useLayoutEffect

我们可以通过useEffect来模拟class组件中的生命周期。

  1. componentDidMount 第二个参数为 []
  2. componentDidUpdate 第二个参数为[xxx] (xxx 更新时调用) 或 不写第二个参数(任意变量更新时调用)
  3. componentWillUnmount 第一个参数中,再次返回一个函数

和useEffect不同的是,useLayoutEffect 是在 DOM ===> 外观 中间调用的

所以useLayoutEffect的调用实际比useEffect早


memo/useMemo / useCallbcak

在一个组件中,含有另一个组件的情况下,

外层的组件如果更新,那么内层的组件也会被重新渲染

useMemo就是用来解决这一问题的,当内层组件没必要重新渲染时,他不会被渲染

const App = () => {
  const [n, setN] = React.useState(0)
  const add = () => {
    setN(i => i + 1)
  }
  return (
    <div>
      {n}
      <button onClick={add}> +1</button>
      <Child2/>
    </div>
  )
}

const Child = () => {
  console.log('hi')
  return (
    <div>Child</div>
  )
}

const Child2 = React.memo(Child)

上面的最后一行代码使用了memo,所以Child不会在随意渲染了,但是,当我们给Child传一个监听函数时,因为每次render时,新旧对象并不 === (全等)

当数组内的更新时,使用了memo的子组件(接受了peops的子组件)才会更新

这时,就需要使用到useMemo了,使用useMemo(缓存值)需要在第二个参数中,声明依赖,即哪个变量变化时,才render Child

 const xxx = React.useMemo(() => {
    return () => {}
  }, [])

上面的代码,需要在函数中返回函数,我们可以使用useCallback(缓存函数)来进行简化

const xxx = React.useCallback( () => {}, [] )

useRef

使用React.useRef 可以实现类似于useState的功能,并且不像useState那样每次更新变量,都会产生一个新的变量。

React.useRef还可以实现获取DOM

  1. 更新同一个变量
const App = () => {
  const divRef = React.useRef(100)
  const [_n, _setN] = React.useState(null)
  const add = () => {
    divRef.current += 1
    _setN(divRef.current)
  }
  return (
    <div>
      {divRef.current}
      <button onClick={add}>+1</button>
    </div>
  )
}

因为useRef 生成了一个变量,所以即使每次都重新渲染,不同的divRef都会指向同一个地址。

同时,更新ref,并不会刷新视图,所以我们需要一个useState来辅助进行视图的刷新。

  1. 获取DOM

在class组件中,我们可以通过React.useRef给另一个组件传递一个ref值,并在父组件中获取到他。

class App extends React.PureComponent {

  render() {
    const divRef = React.createRef()
    return (
      <div>
        <Child ref1={divRef}/>
        <button
          onClick={() => {
            console.log(divRef.current)
          }}>log
        </button>
      </div>
    );
  }
}


class Child extends React.PureComponent {
  constructor(props) {
    super(props);
  }
  render() {
    return (
      <div ref={this.props.ref1}>123</div>
    )
  }
}

在函数组件中,上面的方法就不行了,我们需要通过forwardRef实现

const App = () => {
  const divRef = React.useRef(null)

  return (
    <div>
      <Child2 ref1={divRef}/>
      <button
        onClick={() => {
          console.log(divRef.current)
        }}>log
      </button>
    </div>
  )
}

const Child = (props, ref1) => {
  return (
    <div ref={props.ref1}>child</div>
  )
}

const Child2 = React.forwardRef(Child)