react-hooks中的一些懵逼点

媒介:一直对这个新特征异常感兴趣,终究本日有时候,花了大半天时候,把
Hooks的官方教程过了一遍,收成颇多,赞叹这个新特征真 TM 好用,今后开辟用这个怕是要起飞了😆。

状况钩子(State Hook)

const [state, setState] = useState(initialState);
  1. 多个useState时,React依赖于每次衬着时钩子的挪用递次都是一样的(存在与每一个组件关联的“存储单元”的内部列表寄存JavaScript对象),从而完成钩子与状况的一一对应关联。
  2. setState()吸收新的state或许一个返回state的函数(setCount(prevCount => prevCount - 1)})。
  3. 不同于类组件中的setStateuseState返回的setState 不会自动兼并更新对象到旧的state中(能够运用useReducer)。
  4. useState能够吸收一个函数返回initialState,它只会在首次衬着时被挪用。
  5. setState中的state和当前的state相称(经由过程Object.is推断),将会退出更新。
  6. 发起将一个状况依据哪些须要值一同变化拆分为多个状况变量。

❌用法:

const [rows, setRows] = useState(createRows(props.count));  // `createRows()`每次将会衬着将会被挪用

✅用法:

const [rows, setRows] = useState(() => createRows(props.count));  // `createRows()`只会被挪用一次

个中的() => createRows(props.count)会赋值给rows,如许就保证了只要在rows挪用时,才会建立新的值。

作用钩子(Effect Hook)

useEffect(didUpdate);
  1. 相当于性命周期函数componentDidMount, componentDidUpdate, componentWillUnmount的组合。
  2. 能够返回一个函数(cleanup)用于清算。
  3. 每次从新衬着都将会发作cleanup phase
useEffect(() => {
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });
  componentDidMount() {
    ChatAPI.subscribeToFriendStatus(
      this.props.friend.id,
      this.handleStatusChange
    );
  }

  // ====== 缘由在这里 ======
  componentDidUpdate(prevProps) {
    // Unsubscribe from the previous friend.id
    ChatAPI.unsubscribeFromFriendStatus(
      prevProps.friend.id,
      this.handleStatusChange
    );
    // Subscribe to the next friend.id
    ChatAPI.subscribeToFriendStatus(
      this.props.friend.id,
      this.handleStatusChange
    );
  }

  componentWillUnmount() {
    ChatAPI.unsubscribeFromFriendStatus(
      this.props.friend.id,
      this.handleStatusChange
    );
  }
// Mount with { friend: { id: 100 } } props
ChatAPI.subscribeToFriendStatus(100, handleStatusChange);     // Run first effect

// Update with { friend: { id: 200 } } props
ChatAPI.unsubscribeFromFriendStatus(100, handleStatusChange); // Clean up previous effect
ChatAPI.subscribeToFriendStatus(200, handleStatusChange);     // Run next effect

// Update with { friend: { id: 300 } } props
ChatAPI.unsubscribeFromFriendStatus(200, handleStatusChange); // Clean up previous effect
ChatAPI.subscribeToFriendStatus(300, handleStatusChange);     // Run next effect

// Unmount
ChatAPI.unsubscribeFromFriendStatus(300, handleStatusChange); // Clean up last effect
  1. useEffect(() => {document.title = You clicked ${count} times;}, [count]); ,指定第二个参数(这里为[count])变化时才发作cleanup phase,然后实行effect
  2. 上面状况,假如useEffect第二个参数为为[]则示意只运转一次(componentDidMount中实行effectcomponentWillUnmount中举行cleanup),永久不从新运转。
  3. componentDidMount/componentDidUpdate有区分的处所在于,useEffect中的函数会在layoutpaint完毕后才被触发。(能够运用useLayoutEffect鄙人一次衬着之前(即 DOM 突变以后)同步触发)
  4. useEffect虽然被推晚到浏览器绘制完成以后,然则一定在有任何新的显现之前启动。由于React总是在最先更新之前革新之前衬着的结果。

其他钩子

useContext

const context = useContext(Context);

接收一个上下文对象(由React.createContext建立),返回当前上下文值(由近来的上下文供应)。

附加钩子(Additional Hooks)

基础钩子的变体或用于特定边沿状况的钩子。

useReducer

const [state, dispatch] = useReducer(reducer, initialArg, init);
  1. 第三个参数init为函数,将会如许挪用:init(initialArg),返回初始值。
  2. 假如返回state和如今的state一样,将会在不影响子孙或许触发结果的状况下退出衬着。

useCallback

const memoizedCallback = useCallback(
  () => {
    doSomething(a, b);
  },
  [a, b],
);

传入一个内联回折衷一个输入数组,返回一个带有影象的函数,只要输入数组中个中一个值变化才会变动。useCallback(fn, inputs) 等价于 useMemo(() => fn, inputs)

useMemo

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

传入一个建立函数和一个输入数组,返回一个带有影象的,只要输入数组中个中一个值变化才会从新盘算。

useRef

const refContainer = useRef(initialValue);
// ...
<input ref={refContainer} />
...

返回一个可变的ref对象,能够自动将ref对象中的current属性作为初始值通报的参数,坚持到组件的全部性命周期。

与在类中运用实例字段的体式格局相似,它能够保留任何可变值

如保留前一个状况:

function Counter() {
  const [count, setCount] = useState(0);

  const prevCountRef = useRef();
  useEffect(() => {
    prevCountRef.current = count;
  });
  const prevCount = prevCountRef.current;

  return <h1>Now: {count}, before: {prevCount}</h1>;
}

useImperativeHandle

useImperativeHandle(ref, createHandle, [inputs])

自定在运用 ref 时,公开给父组件的实例值,必需和forwardRef一同运用。

function FancyInput(props, ref) {
  const inputRef = useRef();
  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current.focus();
    }
  }));
  return <input ref={inputRef} ... />;
}
FancyInput = forwardRef(FancyInput);
<FancyInput ref={fancyInputRef} />

// 挪用
fancyInputRef.current.focus()

useLayoutEffect

运用方法和useLayoutEffect一致,不过它是在 DOM 读取规划时同步触发(相当于componentDidMountcomponentDidUpdate阶段)。(发起尽量运用useEffect防止壅塞可视化更新)

useDebugValue

useDebugValue(value)

用于在React DevTools中显现自定义钩子的标签,关于自定义钩子中用于同享的部份有更大代价。

自定义显现花样:

useDebugValue(date, date => date.toDateString());

钩子(Hooks)划定规矩

1. 只能在顶层挪用,不能再循环、前提语句和嵌套函数中运用。 (缘由:State Hook 第1条)

准确做法:

useEffect(function persistForm() {
      // 👍 We're not breaking the first rule anymore
      if (name !== '') {
        localStorage.setItem('formData', name);
      }
    });

2. 只能在React函数组件中被挪用。(能够经由过程自定义钩子函数处理)

能够运用eslint-plugin-react-hooks来强迫自动实行这些划定规矩。

自定义钩子(Hook)

  1. use开首,一种条约。
  2. 自定钩子是一种复用状况逻辑的机制(比方设置定阅和记着当前值),每次运用,内部一切状况和作用都是自力的。
  3. 自定义钩子每一个状况自力的才能源于useStateuseEffect是完整自力的。

测试钩子(Hook)

function Example() {
  const [count, setCount] = useState(0);
  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });
  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

运用ReactTestUtils.act()

import React from 'react';
import ReactDOM from 'react-dom';
import { act } from 'react-dom/test-utils';
import Counter from './Counter';

let container;

beforeEach(() => {
  container = document.createElement('div');
  document.body.appendChild(container);
});

afterEach(() => {
  document.body.removeChild(container);
  container = null;
});

it('can render and update a counter', () => {
  // Test first render and effect
  act(() => {
    ReactDOM.render(<Counter />, container);
  });
  const button = container.querySelector('button');
  const label = container.querySelector('p');
  expect(label.textContent).toBe('You clicked 0 times');
  expect(document.title).toBe('You clicked 0 times');

  // Test second render and effect
  act(() => {
    button.dispatchEvent(new MouseEvent('click', {bubbles: true}));
  });
  expect(label.textContent).toBe('You clicked 1 times');
  expect(document.title).toBe('You clicked 1 times');
});

发起运用react-testing-library

参考

    原文作者:zenghongtu
    原文地址: https://segmentfault.com/a/1190000018253310
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞