【React】Hooks全解之逐个击破

Ⅰ. useState

在React函数组件中,是默认只有属性没有状态的,咱们能够使用以下代码用useState读写变量和设定初始值 :react

const [n,setN] = react.useState(0)redux

const [user,setUser] = react.useState({name:'Jack'})数组

💡 注意事项bash

  1. state不能够局部更新

咱们没法局部setState,由于react函数组件不会帮咱们合并属性。解决办法是使用react提供的...语法将原属性拷贝并放在更新值以前。ide

  1. set的对象必须是新对象

由于setState(obj)obj若是是在原来的对象上进行修改,那么对象的地址没有更改,react会将这个对象断定为原对象,则更改无效。解决方法是始终设置新的对象就能够了。函数

  1. useState接受函数,setState优先使用函数

① useState的初始值并不局限于对象,能够用函数的形式设定。布局

const [state, setState] = useState(()=>{ return initialState })ui

② 因为表达式的局限性,若是你须要在setState中进行屡次操做,那么须要使用函数的形式以确保每一次的操做是有效的。spa

const onClick = ()=>{
    setN(n+1) //无效操做
    setN(n+1) //你会发现 n 不能加 2
  }
复制代码
const onClick = ()=>{
    setN(i=>i+1) //有效操做
    setN(i=>i+1) //最后值为n+2
  }
复制代码

Ⅱ.useReducer

若是你发现有几个变量须要汇总在一块儿,你能够使用复杂版的useState —— useReducer,它的好处就是能够对这个对象进行一个总体的操做 。而且能够践行React社区一直推崇的Flux/Redux思想,在我看来这个思想可能会在不久的未来Hooks思想盛行以后逐渐被取代,可是目前仍是须要了解这个被普遍应用的Hook。翻译

使用useReducer分为四个步骤:

  • 建立初始值initialState
  • 建立全部操做reducer(state,action)
  • 传给useReducer获得读和写API
  • 调用写({type:"操做类型"})

一个🌰:

const initial = {
    n:0
};

const reducer = (state,action) => {
    if(action.type === "add") {
        return { n:state.n + 1 };
    } 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: "multi", number: 2 });
      };
      return (
      <div className = "App">
        <div>n: {n}</div>
        <button onClick={onClick}>+1</button>
        <button onClick={onClick2}>*2</button>
      </div>    
    );
}
复制代码

如何代替redux?

  1. 将数据集中在一个store对象;
  2. 将全部操做集中在reducer;
  3. 建立一个Context;
  4. 建立对数据读写的API;
  5. 将第四步的操做放进Context中;
  6. 用Context.Provider将Context提供给i全部组件;
  7. 各个组件用useContext获取读写API。

Ⅲ.useContext

这个Hook翻译过来就是“上下文”。何为上下文?上下文就是你在运行一个程序的时候所要知道的全部变量。全局变量就是全局的上下文,上下文是局部的全局变量。上一个Hook中咱们已经了解到useContext的用法。

一个🌰:

const C = createContext(null);

function App() {
  const [n, setN] = useState(0);
  return (
    <C.Provider value={{ n, setN }}>
      <div className="App">
        <Dog />
      </div>
    </C.Provider>
  );
}

function Dog() {
  const { n, setN } = useContext(C);
  return (
    <div>
      狗 n: {n} <Child />
    </div>
  );
}

function Child() {
  const { n, setN } = useContext(C);
  const onClick = () => {
    setN(i => i + 1);
  };
  return (
    <div>
      狗儿子 n: {n}
      <button onClick={onClick}>+1</button>
    </div>
  );
}
复制代码

Ⅳ.useEffect

这个Hook翻译过来为“反作用”,我认为把它叫作afterRender更好,由于这是每次render以后就会调用的一个函数。 用途:

  • 做为componentDidMount(出生后)使用

    useEffect(()=>{console.log('hi'),[]}) //使用空数组表示第一次渲染时

  • 做为componentUpdate(更新)使用

    useEffect(()=>{console.log('hi'),[n]}) //表示当{n}变化时

    useEffect(()=>{console.log('hi')}) //表示每一次更新时

  • 做为componentWillUnmount(将死)使用

    useEffect(()=>{console.log('hi')})
    return ()=>{} //添加return表示当组件将时执行某操做
    复制代码

useLayoutEffect

  • 布局反作用:useEffect在渲染后执行,useLayoutEffect在渲染前执行。
  • 特色: useLayoutEffect老是比useEffect先执行。
  • 经验: 为了用户体验,优先使用useEffect(优先渲染)。

Ⅴ.useMemo & useCallback

因为react默认会有多余的render出现,若是props不变就没要再次执行这个函数组件,useMemo就是阻止函数组件无效执行的Hook。

❗❗ 若是在函数组件中添加一个监听事件,则useMemo会直接无效,须要写成:

useMemo(()=> (x) => console.log(x))这种形式,一个返回函数的函数。 因为这种方式难用且难以理解,因而诞生了useCallback

useCallback(x => log(x), [m]) //等价于下面一种写法
useMemo(() => x => log(x), [m])
复制代码

Ⅵ.useRef

若是你须要一个值在组件不断render的时候保持始终是同一个值地址不变,那就须要用到useRef。

首先须要初始化:const n = useRef(0)

读取时则是:n.current (这里的current是用来保证两次useRef的是相同的值的引用)

❗❗ useRef是不能作到变化的时候自动render的,由于这不符合react的理念,若是你想要这个功能,彻底能够本身加,监听ref,当ref.current变化的时候,调用setX便可。

Ⅶ.自定义Hook

自定义Hook必须以use开头,这是react既定的语法。经过自定义 Hook,能够将组件逻辑提取到可重用的函数中。

一个🌰:

function App() {
  const { list } = useList();
  return (
    <div className="App">
      <h1>List</h1>
      {list ? (
        <ol>
          {list.map(item => (
            <li key={item.id}>{item.name}</li>
          ))}
        </ol>
      ) : (
        "加载中..."
      )}
    </div>
  );
}
复制代码

与 React 组件不一样的是,自定义 Hook 不须要具备特殊的标识。咱们能够自由的决定它的参数是什么,以及它应该返回什么(若是须要的话)。换句话说,它就像一个正常的函数,咱们能够最大限度的灵活使用这个Hook。

相关文章
相关标签/搜索