ReactHooks

392 阅读3分钟

useState

  • 使用状态
const [n,setN]=React.useState(0)
const [user.setUser]=React.useState({name:'A'})
  • 注意事项1:不可以局部更新

例: 如果我们只setUser的name时点击click会出现如下情况: 为了保证全部更新需要把之前的属性拷贝一份,做法如下:

import React, {useState} from "react";
import ReactDOM from "react-dom";

function App() {
  const [user,setUser] = useState({name:'Dog', age: 3})
  const onClick = ()=>{
    setUser({
      ...user, // 这里实现拷贝之前的属性
      name: 'Jack'
    })
  }
  return (
    <div className="App">
      <h1>{user.name}</h1>
      <h2>{user.age}</h2>
      <button onClick={onClick}>Click</button>
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
  • 注意事项2: 地址要变

setState(obj)如obj的地址不变,React就认为数据没有发生变化

  • useState接受函数
const [state, setState] = useState(()=>{
return initialState
})
setN(i => i + 1)}

useReducer

  • 用来践行Flux/Redux的思想

    分四步:

    1,创建初始值initialState

    2,创建所有操作

    3传给useReducer,得到读和写API

    4,调用写({type:操作类型})

    import React, { useState, useReducer } from "react";
    import ReactDOM from "react-dom";
    //初始值
    const initial = {
    n: 0
    };
    // 创建操作
    const reducer = (state, action) => {
    if (action.type === "add") {
      return { n: state.n + action.number };
    } else if (action.type === "multi") {
      return { n: state.n * 2 };
    } else {
      throw new Error("unknown type");
    }
    };
    
    function App() {
    const [state, dispatch] = useReducer(reducer, initial); //得到都和写的API
    const { n } = state;
    const onClick = () => { 
      dispatch({ type: "add", number: 1 });  //调用操作类型
    };
    const onClick2 = () => {
      dispatch({ type: "multi", number: 2 });
    };
    return (
      <div className="App">
        <h1>n: {n}</h1>
    
        <button onClick={onClick}>+1</button>
        <button onClick={onClick2}>+2</button>
      </div>
    );
    }
    
    const rootElement = document.getElementById("root");
    ReactDOM.render(<App />, rootElement);
    

    一个用useReducer的表单例子 链接

    useContext

  • 上下文

    全局变量是全局的上下文

    上下文是局部的全局变量

  • 使用方法

1,使用C=createContext(initial)创建上下文

2,使用<C.provider>圈定作用域

3,在作用域内使用useContext(c)来使用上下文 代码链接

** 注意**:不是响应式的,在一个模块将C里面的值改变,另一个模块不会感知这个变化

import React, { createContext, useState, useContext } from "react";
import ReactDOM from "react-dom";

const C=createContext(null) //创建上下文

function App(){
  const[n,setN]=useState(0)
  return(
    //圈定作用域
    <C.Provider value={{n,setN}}>  
      {n}
      <Parent/>
      <Child/>
      </C.Provider>
  )
}
function Parent(){
  return (
    <div>
      我是爸爸
      </div>
  )
}
function Child(){
  // 3,在作用域内使用
  const {n,setN}=useContext(C)
  const onClick=()=>{
    setN(i=>i+1)
  }
  return (
    <div>
      我是儿子
      <button onClick={onClick}>+1</button>
      </div>
  )

}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

useEffect

  • 副作用(API名字叫的不大好)

    1,对环境的改变即为副作用,如修改doucument.title,但不一定将副作用放在useEffect里,实际上叫afterRender更好,每次render后运行

  • 用途:

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

    2,作为componentDidUpdate使用,可指定依赖

    3,作为componentWillUnmount使用,通过return

  • 特点

如果存在多个useEffect,会按照次序执行 效果链接

import React, { createContext, useState, useContext, useEffect } from "react";
 import ReactDOM from "react-dom";
 function App(){
   const [n,setN]=useState(0)
   const onClick=()=>{
     setN(i=>i+1)
   }
   useEffect(()=>{
     console.log('第一次渲染后执行')
   },[]) // 里面的变量变化执行=>不会执行
   useEffect(()=>{
     if(n!==0){
       console.log('n变化了')
     }
   },[n]) //n变化时执行
   useEffect(()=>{
     console.log('任何一个state变化都执行')
   })
   return  (
     <div>
       n:{n}
       <button onClick={onClick}>+1</button>
       </div>
   )
 }
 const rootElement = document.getElementById("root");
 ReactDOM.render(<App />, rootElement);

useLayoutEffect

  • 布局副作用

    useEffect在渲染完成后执行,useLayoutEffect在浏览器渲染前执行。

    通过时间点来证明

  • 特点

1,useLayoutEffect总是比useEffect先执行

  • 经验

    1,为了用户体验优先使用useEffect(有限渲染)

useRef

  • 目的:设定一个值在组件不断render时保持不变,
  • 初始化:const count=useRef(0)
  • 读取:count.current
  • current是为了保证两次useRef是同一个值

forwardRef

  • props无法传递ref属性情况
  import React, { useRef } from "react";
  import ReactDOM from "react-dom";

  import "./styles.css";

  function App() {
    const buttonRef = useRef(null);
    return (
      <div className="App">
        <Button2 ref={buttonRef}>按钮</Button2>
        {/* 看浏览器控制台的报错 */}
      </div>
    );
  }

  const Button2 = props => {
    return <button className="red" {...props} />;
  };

  const rootElement = document.getElementById("root");
  ReactDOM.render(<App />, rootElement);
  • 实现ref传递
  import React, { useRef } from "react";
  import ReactDOM from "react-dom";

  import "./styles.css";

  function App() {
    const buttonRef = useRef(null);
    return (
      <div className="App">
        <Button3 ref={buttonRef}>按钮</Button3>
      </div>
    );
  }

  const Button3 = React.forwardRef((props, ref) => {
    return <button className="red" ref={ref} {...props} />;
  });

  const rootElement = document.getElementById("root");
  ReactDOM.render(<App />, rootElement);