React Hooks

48 阅读3分钟

useState

  1. 使用状态
  • const [n,setN] = React.useState(0)
  • const [user,setUser] = React.useState({name:'F'})
  1. 注意事项
  • 如果state是一个对象,不能部分setState,因为setState不会帮我们合并属性
  • setState(obj)如果obj地址不变,那么React就认为数据没有变化
  • setState接受函数,setN(i => i + 1)
import React, { useState } from "react";
import ReactDOM from "react-dom";

function App() {
  const [n, setN] = useState(0);
  const onClick = () => {
    // setN(n + 1);
    // setN(n + 1); // 你会发现 n 不能加 2
    setN(i=>i+1)
    setN(i=>i+1)
  };
  return (
    <div className="App">
      <h1>n: {n}</h1>

      <button onClick={onClick}>+2</button>
    </div>
  );
}

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

export default App;

useReducer

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)
    const {n} = state
    const onClick = ()=>{
        dispatch({type: 'add', number:1})
    }
    const onClick2 = ()=>{
        dispatch({type: 'add', 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);

export default App;

用useReducer的表单例子:

const initFormData = {
  name: "",
  age: 18,
  nationality: "汉族",
};

function reducer(state, action) {
  switch (action.type) {
    case "patch":
      return { ...state, ...action.formData };
    case "reset":
      return initFormData;
    default:
      throw new Error();
  }
}

function App3() {
  const [formData, dispatch] = useReducer(reducer, initFormData);
  // const patch = (key, value)=>{
  //   dispatch({ type: "patch", formData: { [key]: value } })
  // }
  const onSubmit = () => {};
  const onReset = () => {
    dispatch({ type: "reset" });
  };
  return (
    <form onSubmit={onSubmit} onReset={onReset}>
      <div>
        <label>
          姓名
          <input
            value={formData.name}
            onChange={(e) =>
              dispatch({ type: "patch", formData: { name: e.target.value } })
            }
          />
        </label>
      </div>
      <div>
        <label>
          年龄
          <input
            value={formData.age}
            onChange={(e) =>
              dispatch({ type: "patch", formData: { age: e.target.value } })
            }
          />
        </label>
      </div>
      <div>
        <label>
          民族
          <input
            value={formData.nationality}
            onChange={(e) =>
              dispatch({
                type: "patch",
                formData: { nationality: e.target.value },
              })
            }
          />
        </label>
      </div>
      <div>
        <button type="submit">提交</button>
        <button type="reset">重置</button>
      </div>
      <hr />
      {JSON.stringify(formData)}
    </form>
  );
}

useReducer代替redux

//App.js
import Context from "./Context";
import User from "./components/user";
import Books from "./components/books";
import Movies from "./components/movies";
import userReducer from "./reducers/user_reducer";
import booksReducer from "./reducers/books_reducer";
import moviesReducer from "./reducers/movies_reducer";
const store = {
  user: null,
  boosk: null,
  movies: null,
};

const obj = {
  ...userReducer,
  ...booksReducer,
  ...moviesReducer,
};
function reducer(state, action) {
  const fn = obj[action.type];
  if (fn) {
    return fn(state, action);
  } else {
    throw new Error("报错了");
  }
}
/*
const reducer = (state, action) => {
  switch (action.type) {
    case "setUser":
      return { ...state, user: action.user };
    case "setBooks":
      return { ...state, books: action.books };
    case "setMovies":
      return { ...state, movies: action.movies };
    default:
      throw new Error();
  }
};
*/
function App() {
  const [state, dispatch] = useReducer(reducer, store);
  return (
    <Context.Provider value={{ state: state, dispatch: dispatch }}>
      <User />
      <hr />
      <Books />
      <Movies />
    </Context.Provider>
  );
}

const div = document.getElementById("root");
ReactDOM.render(<App />, div);
//user_reducer.js
export default {
  setUser: (state, action) => {
    return { ...state, user: action.user };
  },
};
//user.js
function User() {
  const { state, dispatch } = useContext(Context);
  useEffect(() => {
    ajax("/user").then((user) => {
        console.log('user',user)
      dispatch({ type: "setUser", user: user });
    });
  }, []);

  return (
    <div>
      <h1>个人信息</h1>
      <div>name: {state.user ? state.user.name : ""}</div>
    </div>
  );
}

export default User;

useContext

import React from "react";
const Context = React.createContext(null);
export default Context;
function App() {
  const [state, dispatch] = useReducer(reducer, store);
  return (
    <Context.Provider value={{ state: state, dispatch: dispatch }}>
      <User />
      <hr />
      <Books />
      <Movies />
    </Context.Provider>
  );
}

useEffect

用途

  • 作为componentDidMount使用,[ ]作第二个参数
  • 作为componentDidUpdate使用,可指定依赖
  • 作为componentWillUnmount使用,通过return
  • 以上三种用途可同时存在

特点 如果同时存在多个useEffect,会按照出现次序执行

function UseEffectDemo() {
    const [n, setN] = useState(0);
    const onClick = ()=>{
        setN(i => i + 1)
    }
    const afterRender = useEffect;
    afterRender(() => {
        console.log("第一次渲染之后执行这一句话");
    },[])
    afterRender(()=>{
        console.log("任何一个state变化时都执行");
    })
    afterRender(()=>{
        console.log("n变化了");
    },[n])
    return (
        <div>
            n:{n}
            <button onClick={onClick}>+1</button>
        </div>
    )
}

useMemo

import React, { useMemo } from "react";

function UseMemoDemo() {
  const [n, setN] = React.useState(0);
  const [m, setM] = React.useState(0);
  const onClick = () => {
    setN(n + 1);
  };
  const onClick2 = () => {
    setM(m + 1);
  };
  const onClickChild = useMemo(() => {
    const fn = (div) => {
      console.log("on click child, m: " + m);
      console.log(div);
    };
    return fn;
  }, [m]);
  return (
    <div className="App">
      <div>
        <button onClick={onClick}>update n {n}</button>
        <button onClick={onClick2}>update m {m}</button>
      </div>
      <Child2 data={m} onClick={onClickChild} />
    </div>
  );
}

function Child(props) {
  console.log("child 执行了");
  return (
    <div onClick={(e) => props.onClick(e.target)}>child: {props.data}</div>
  );
}

const Child2 = React.memo(Child);

export default UseMemoDemo;

useRef

import React, { useEffect, useMemo, useRef } from "react";

function UseRefDemo() {
    console.log("App 执行");
    const count = useRef(0);
    const [n, setN] = React.useState(0);
    const onClick = ()=>{
        setN(n+9);
    }
    useEffect(()=>{
        count.current += 1;
        console.log(count.current);
    })
    return (
        <div className="App">
            <div>
                <button onClick={onClick}>update n {n}</button>
            </div>
        </div>
    )
}

export default UseRefDemo;

forwardRef

import React, { useEffect, useMemo, useRef } from "react";

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

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

export default ForwardRefDemo;

自定义Hook

//useList.js
import { useState, useEffect } from "react";

const useList = () => {
  const [list, setList] = useState(null);
  useEffect(() => {
    ajax("./list").then((list) => {
      console.log("list", list);
      setList(list);
    });
  }, []); //确保只在第一次运行
  return {
    list: list,
    setList: setList,
    addItem: (name) => {
      setList([...list, { id: Math.random(), name: name }]);
    },
    deleteIndex: (index) => {
      setList(list.slice(0, index).concat(list.slice(index + 1)));
    },
  };
};
export default useList;

function ajax() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve([
        { id: 1, name: "Mike" },
        { id: 2, name: "Jack" },
        { id: 3, name: "Alice" },
        { id: 4, name: "Bob" },
      ]);
    }, 2000);
  });
}

import React, { useEffect, useReducer, useState } from "react";
import ReactDOM from "react-dom";
import useList from "./hooks/useList";

function UseListHooksDemo() {
  const { list, deleteIndex, addItem } = useList();
  return (
    <div className="App">
      <h1>List</h1>
      {list ? (
        <ol>
          {list.map((item, index) => (
            <li key={item.id}>
              {item.name}
              <button
                onClick={() => {
                  deleteIndex(index);
                }}
              >
                x
              </button>
            </li>
          ))}
        </ol>
      ) : (
        "加载中..."
      )}
    </div>
  );
}
export default UseListHooksDemo;