React Hooks速成

1、useReducer

适用情况为对一个状态多种复杂操作,通俗的讲就是比如对count这个变量加减乘除的各种情况

改造前

import { useState } from "react";

function App() {
  //计数器
  const [count, setCount] = useState(0);
  const handleIncrement = () => {
    setCount(count + 1);
  };
  const handleDecrement = () => {
    setCount(count - 1);
  };
  return (
    <div style={{ padding: 10 }}>
      <button onClick={handleIncrement}>+</button>
      <span>{count}</span>
      <button onClick={handleDecrement}>-</button>
    </div>
  );
}

export default App;

改造后

import { useReducer } from "react";
function countReducer(state, action) {
  switch (action.type) {
    case "increment":
      return state + 1;
    case "decrement":
      return state - 1;
    default:
      throw new Error();
  }
}
function App() {
  //计数器
  const [state, dispatch] = useReducer(countReducer, 0);
  const handleIncrement = () => {
    dispatch({ type: "increment" });
  };
  const handleDecrement = () => {
    dispatch({ type: "decrement" });
  };
  return (
    <div style={{ padding: 10 }}>
      <button onClick={handleIncrement}>+</button>
      <span>{state}</span>
      <button onClick={handleDecrement}>-</button>
    </div>
  );
}

export default App;

2、useRef

ref记住状态变更之前的值

import { useRef, useState } from "react";

function App() {
  const [count, setCount] = useState(0);
  const prevCount = useRef();

  function handleClick() {
    prevCount.current = count;
    setCount(count + 1);
  }
  return (
    <div>
      <p>当前的count:{count}</p>
      <p>上一次的count:{prevCount.current}</p>
      <button onClick={handleClick}>增大count</button>
    </div>
  );
}

export default App;

ref获取标签

import { useRef } from "react";
function App() {
  const inputRef = useRef(null);
  function handleClick() {
    inputRef.current.focus();
  }
  return (
    <div>
      <input type="text" ref={inputRef} />
      <button onClick={handleClick}>按钮</button>
    </div>
  );
}

export default App;

ref获取其他子组件

需要以下步骤

1、子组件不能定义为一个普通函数,必须使用函数表达式的方式

2、这个函数表达式需要forwardRef包裹处理,这样才可以被父组件使用

3、父组件给子组件传入ref

4、子组件中的方法还需要useImperativeHandle包裹处理,才能被父组件调用

import { useRef, forwardRef, useImperativeHandle } from "react";
const Child = forwardRef(function (props, ref) {
  useImperativeHandle(ref, () => ({
    //暴露给父组件的方法
    myFn: () => {
      console.log("子组件myFn的方法");
    },
  }));
  return <div>子组件</div>;
});
function App() {
  const childRef = useRef();
  function handleClick() {
    childRef.current.myFn();
  }
  return (
    <div>
      <Child ref={childRef} />
      <button onClick={handleClick}>点击</button>
    </div>
  );
}

export default App;

3、useEffect

React要求所有的函数式组件都是纯函数,意味同样的输入就有同样的输出

如果想要设置副作用的话,那我们可以使用useState事件这种

如果想要在组件加载或者组件更新时(非用户操作触发),能够执行一些副作用的话,需要用到useEffect

useEffect在React严格模式下默认会执行两次

如果想要在组件渲染的时候执行一次,以后就不再变更的话,可以给useEffect传递一个空的依赖数组,如果数组中填写了一些状态的话,这些状态的变化会导致副作用的重新执行

import { useEffect, useState } from "react";
function App() {
  const [count, setCount] = useState(0);
  const handleIncrement = () => {
    setCount(count + 1);
  };
  const handleDecrement = () => {
    setCount(count - 1);
  };
  useEffect(() => {
    console.log("useEffect");
  }, [count]);
  return (
    <div style={{ padding: 10 }}>
      <button onClick={handleIncrement}>+</button>
      <span>{count}</span>
      <button onClick={handleDecrement}>-</button>
    </div>
  );
}

export default App;

4、useMemo

一种用来缓存数据的钩子

以下代码,父组件改变count的值时,父组件会重新渲染,子组件也会随着父组件渲染,但是子组件并没有状态变更,不需要重新渲染,这没有意义,所以需要使用useMemo包裹处理需要缓存的那个数据,来监听那个数据有没有变化,使得子组件没有状态变更就不重新渲染,提高性能

import { useState } from "react";
function DoSomeMath({ value }) {
  console.log("DoSomeMath执行了");
  let result = 0;
  for (let i = 0; i < 1000000; i++) {
    result += value * 2;
  }
  return (
    <div>
      <p>输入内容:{value}</p>
      <p>经过复杂计算的数据:{result}</p>
    </div>
  );
}
function App() {
  const [inputValue, setInputValue] = useState(5);
  const [count, setCount] = useState(0);
  return (
    <div>
      <p>count的值为:{count}</p>
      <button onClick={() => setCount(count + 1)}>点击更新</button>
      <br />
      <br />
      <input
        type="number"
        value={inputValue}
        onChange={(e) => setInputValue(parseInt(e.target.value))}
      />
      <DoSomeMath value={inputValue} />
    </div>
  );
}

export default App;

优化后的代码

import { useState } from "react";
import { useMemo } from "react";
function DoSomeMath({ value }) {
  const result = useMemo(() => {
    console.log("DoSomeMath执行了");
    let result = 0;
    for (let i = 0; i < 1000000; i++) {
      result += value * 2;
    }
    return result;
  }, [value]);
  return (
    <div>
      <p>输入内容:{value}</p>
      <p>经过复杂计算的数据:{result}</p>
    </div>
  );
}
function App() {
  const [inputValue, setInputValue] = useState(5);
  const [count, setCount] = useState(0);
  return (
    <div>
      <p>count的值为:{count}</p>
      <button onClick={() => setCount(count + 1)}>点击更新</button>
      <br />
      <br />
      <input
        type="number"
        value={inputValue}
        onChange={(e) => setInputValue(parseInt(e.target.value))}
      />
      <DoSomeMath value={inputValue} />
    </div>
  );
}

export default App;

5、useCallback

一种用来缓存函数的钩子

以下代码当父组件更新count状态时,父组件会重新渲染,所以handleClick会重新创建变成一个新的函数,那么传给子组件的handleClick也会变成新的props,但实际上父组件传给子组件的props没有变化,但是子组件会重新渲染
要解决这个问题,有两个步骤要做:
a、将要缓存的函数使用memo记忆体包裹,并且写成函数表达式的形式,变成记忆组件。memo的作用是当传入的prop是同一个没有变化时,就不会让子组件重新渲染

b、使用useCallback包裹父组件的那个传入子组件的函数,使得父组件重新渲染时,因为缓存了该函数,不会重新创建该函数,使得组件传入的props没变,最终使得子组件不会被重新渲染
 

import { useState } from "react";
function Button({ onClick }) {
  console.log("Button渲染了");
  return <button onClick={onClick}>子组件</button>;
}
function App() {
  const [count, setCount] = useState(0);
  const handleClick = () => {
    console.log("点击按钮");
  };
  const handleUpdate = () => {
    setCount(count + 1);
  };
  return (
    <div>
      <p>Count:{count}</p>
      <button onClick={handleUpdate}>点击</button>
      <br />
      <Button onClick={handleClick}></Button>
    </div>
  );
}

export default App;

优化后的代码

import { memo, useCallback, useState } from "react";
const Button = memo(function ({ onClick }) {
  console.log("Button渲染了");
  return <button onClick={onClick}>子组件</button>;
});
function App() {
  const [count, setCount] = useState(0);
  const handleClick = useCallback(() => {
    console.log("点击按钮");
  }, []); //依赖项为空数组,表示该函数只在组件挂载时创建一次
  const handleUpdate = () => {
    setCount(count + 1);
  };
  return (
    <div>
      <p>Count:{count}</p>
      <button onClick={handleUpdate}>点击</button>
      <br />
      <Button onClick={handleClick}></Button>
    </div>
  );
}

export default App;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值