记 19 React Hooks

124 阅读5分钟

Hooks: useState状态管理

记 17 中记录了Hooks: useState状态管理的使用

Hooks: useContext状态传递

记 18 中记录了Hooks: useContext状态传递的使用

Hooks: useReducer同一状态管理

useReducer 和 useState 非常相似,可以更好的管理同一状态的多种复杂操作。而且 它可以让你把状态更新逻辑从事件处理函数中移动到组件外部。

import { useReducer } from 'react';

//第一个参数表示reducer当前要管理的状态,第二个参数表示要对当前状态做的操作
function countReducer(state, action) {
  switch (action.type) {
    case 'add':
      return state + 1;
    case 'minus':
      return state - 1;
    default:
      return state;
  }
}

export default function App() {
  //返回数组的第一个参数表示状态初始值,第二个参数是用来进行状态修改的状态触发器
  const [state, dispatch] = useReducer(countReducer, 0);

  return (
    <>
      <button onClick={() => {
        dispatch({ type: 'minus' })
      }} > - </button>
      <span>Hello! state {state}.</span>
      <button onClick={() => {
        dispatch({ type: 'add' })
      }} > + </button>
    </>
  );
}

Hooks: useRef

记录并引用之前的状态值

import { useRef, useState } from 'react';

export default function App() {
  //返回数组的第一个参数表示状态初始值,第二个参数是用来进行状态修改的状态触发器
  const [count, setCount] = useState(0);

  const prevCount = useRef();
  // 设置preCount.current,本质是调用useRef返回的一个可变的ref对象,其.current属性被初始化为传入的参数(用来记住count的值)。它的值可以做修改,但并不是响应式的状态

  function handleClick() {
    prevCount.current = count;
    setCount(count + 1)
  }

  return (
    <>
      <p>Hello! new count! {count}. </p>
      <p>old count! {prevCount.current}. </p>
      <button onClick={handleClick} >+1</button>
    </>
  );
}

记录并引用页面中的标签

export default function App() {
  const inputRef = useRef(null);
  function handleClick() {
    inputRef.current.focus();
  }

  return (
    <>
      <input type="text"  ref={inputRef}/>
      <button onClick={handleClick} >按钮</button>
    </>
  );
}

记录并引用页面中其他组件

react中默认子组件是不对外开放的。因此我们不能直接使用ref来获取子组件的实例对象,如果我们想要访问其他组件内部的功能,需要进行以下设置

  1. 必须使用 forwardRef方法(高阶组件)定义函数 需要在子组件中使用 forwardRef(高阶组件)定义函数内容,它可以将子组件中绑定ref的DOM转发给父组件。
  2. 使用 useImperativeHandle方法 暴露子组件内部的函数功能及其他属性方法
import { useImperativeHandle, forwardRef, useRef} from 'react';
//forwardRef接收一个函数作为参数,该函数接收两个参数,第一个参数是子组件的props,第二个参数是子组件中传递给父组件的绑定ref的DOM
const Child = forwardRef(function (props, ref) {
  //useImperativeHandle接收两个参数,第一个参数是绑定ref的DOM,第二个参数是一个函数,该函数返回一个对象,该对象就是子组件暴露给父组件的属性方法,需要用小括号将对象包裹起来
  useImperativeHandle(ref, () => ({
    //暴露给父组件的属性方法
    myFn: () => {
      //子组件的方法
      console.log('子组件的方法');
    }
  }))
  return (
    <div>
      <p>我是子组件</p>
    </div>
  )

})
export default function App() {
  const childRef = useRef();
  function handleClick() {
    childRef.current.myFn();
  }

  return (
    <>
      <Child ref={childRef} />
      <button onClick={handleClick} >按钮</button>
    </>
  );
}

Hooks: useEffect

副作用函数。例如我们需要在组件加载、更新等非用户触发类事件上添加副作用事件(执行接口请求)时,我们就可以使用useEffect钩子

  useEffect(() => {
    // 组件挂载时执行
    console.log('App 组件挂载');
    return () => {
      console.log('App 组件卸载');
    }
    //第二个参数传入依赖数组,只有当依赖数组中的值发生变化时才会执行,空数组表示组件挂载时执行一次,任何情况都不再执行
    //如传入count,那么当count发生变化时就会执行
  }, [count]);

Hooks: useMemo

useMemo是用来进行数据缓存的钩子。react中子组件会随父组件状态的变化而变化,当子组件中需要进行复杂计算时,如果父组件状态变更导致重新渲染,子组件也会重新执行计算操作,哪怕数据并没有发生变化。这时我们就可以使用useMemo将计算结果进行缓存

//父组件中设置几个状态项 如count和input 当我们不希望改变count状态值后子组件中input的计算属性重新渲染,就可以使用useMemo
//子组件中
function Computed({ value }) {
  //每次操作后都会将计算结果缓存到result中
  const result = useMemo(() => {
    console.log('计算中...');
    // 计算
    return value * 2;
    //第二个参数是依赖项数组,当数组中指定代码发生变化后,才会重新计算
  }, [value]);
  return (
    <div>
      <p>input: {value}</p>
      <p>result: {result}</p>
    </div>
  )
}

Hooks: useCallBack

useCallBack用来缓存函数,同样的,当父组件渲染时子组件也同时被重新渲染,可以将子组件设置为记忆组件,

  1. 使用react的memo函数将组件变更为记忆组件
  2. 使用useCallback钩子来保证函数的不变性 通过这些步骤,当父组件变更时,传入子组件的函数也不会发生变化,子组件就不会重新渲染
import { memo, useMemo, useState } from 'react';

//设置子组件为记忆组件,这样当传入的props没有发生变化时,子组件就不会重新渲染
const Button = memo(function Button({ onClick }) {
  console.log('button render')
  return (
    <div>
      <button onClick={onClick}>子组件</button>
    </div>
  )
})
export default function App() {
  const [count, setCount] = useState(0);
  //父组件渲染后,组件中的函数也变成了新的函数,所以需要在APP重新渲染的时候保证handleClick是不变的(prop不发生变化,子组件就不会重新渲染)
  //使用useCallback来保证函数的不变性
  const handleClick = useCallback(() => {
    console.log('父组件')
  }, [])
  const handleUpdata = () => {
    setCount(count + 1)
    console.log('更新数据')
  }
  return (
    <>
      <p>count的值{count}</p>
      <button onClick={handleUpdata} >count + 1</button>
      <br />
      <Button onClick={handleClick} >count + 1</Button>
    </>
  );
}