你可能不知道的react

704 阅读5分钟

本文列举了一些个人认为经典的文章,可以加深对react原理,以及如何正确使用react的理解。很多文章基本就是Dan的blog扒出来,如果有时间,建议大家直接读Dan的blog

原理相关

react as a ui runtim

运行时讲解react如何工作,不太深,算是一个概览。mark个有意思的点,是我之前也没有考虑过的。 JSX使得惰性求值成为可能,本身也是依赖倒置的易用体现(react帮我们createElement,做协调)


从源码深入探究React 运行时优化方案的演进

经典好文,没事可以复习一下。15-6-7-8,react到底升级了什么,想要从哪些方面提升体验。


Why Do React Hooks Rely on Call Order?

很经典的面试题,解释了为什么react不能在条件或循环语句中调用。但是注意,这不是从实现(链表)的角度来解释的,而是解释其他方案为什么行不通,所以才有了链表这样的设计。

怎么用好react

其实下面大部分内容,都应该是怎么用好hooks,简而言之:UI=f(data)心智模型。 每一次render,react都是根据当前的props和state生成期望的react tree,然后再做tree到dom的同步,useEffect也是类似,只不过是同步到react tree之外的东西罢了

React hooks FAQ

React hooks FAQ回答了很多我们常见的开发问题,值得一看。比如怎么拿到上一次渲染的props或者state。

function Counter() {
  const [count, setCount] = useState(0);
  const prevCount = usePrevious(count);  return <h1>Now: {count}, before: {prevCount}</h1>;
}

function usePrevious(value) {  const ref = useRef();
  useEffect(() => {
    ref.current = value;
  });
  return ref.current;
}

A Complete Guide to useEffect

建议反复看,时不时复习一下。关键就是,使用Class components和Function Component是两种心智模型。“think in effects”,它的心智模型更接近于实现状态同步,而不是响应生命周期事件。 以下是几个重要的结论:

  1. React 会根据我们当前的props和state同步到DOM 。 “mount”和“update”之于渲染并没有什么区别。我们应该以相同的方式去思考effectsuseEffect使我们能够根据props和state同步React tree之外的东西。

  2. effect是在渲染完成后再执行的,包括清理。所以,执行时机是:

    1. React 渲染new的UI 。
    2. 浏览器绘制。我们在屏幕上看到 new的UI。
    3. React 清除old的effect。
    4. React 运行new的effect。
  3. 当我们写类似setSomething(something => ...)这种代码的时候,也许就是考虑使用reducer的契机。reducer可以让我们把组件内发生了什么(actions)和状态如何响应并更新分开表述

  1. 为什么useReducer是Hooks的作弊模式?
function Counter({ step }) {
  const [count, dispatch] = useReducer(reducer, 0);

  function reducer(state, action) {
    if (action.type === 'tick') {
      return state + step;    } else {
      throw new Error();
    }
  }

  useEffect(() => {
    const id = setInterval(() => {
      dispatch({ type: 'tick' });
    }, 1000);
    return () => clearInterval(id);
  }, [dispatch]);

  return <h1>{count}</h1>;
}

Your Guide to React.useCallback()

几种典型的可能需要使用useCallback的场景:

  1. 包装在 React.memo()(或 shouldComponentUpdate )中的组件接受回调prop。

  2. 当函数用作其他hooks的依赖项时 useEffect(...,[callback])

在使用useCallback前,可以先用profile看看能带来多大的性能优化,关于useCallback的效果可以看看下面这个demo。

codesandbox.io/s/competent…


Making setInterval Declarative with React Hooks

直接上结果,本质其实是命令式到申明式的转换adapter

import React, { useState, useEffect, useRef } from 'react';

function useInterval(callback, delay) {
  const savedCallback = useRef();

  // 保存新回调
  useEffect(() => {
    savedCallback.current = callback;
  });

  // 建立 interval
  useEffect(() => {
    function tick() {
      savedCallback.current();
    }
    if (delay !== null) {
      let id = setInterval(tick, delay);
      return () => clearInterval(id);
    }
  }, [delay]);
}

可以思考一个小问题,下面的改动会导致什么结果?

import React, { useState, useEffect, useRef } from 'react';

function useInterval(callback, delay) {
  const savedCallback = useRef();

  // 保存新回调
  useEffect(() => {
    savedCallback.current = callback;
  });

  // 建立 interval
  useEffect(() => {
    // function tick() {
    //   savedCallback.current();     
    // }
    if (delay !== null) {
      let id = setInterval(savedCallback.current, delay);
      return () => clearInterval(id);
    }
  }, [delay]);
}

How to fetch data with React Hooks

由简入繁,层层递进,通过不断演进fetch数据的需求,讲解怎么正确地使用hook fetch数据。


Before You memo()

除了使用memo,通过简单的组件拆分,也能达到优化的目的:

  1. state下沉。

  2. JSX as children。


Writing Resilient Components

编写弹性(可伸缩)的组件,基本含义就是可复用。

  1. 不要阻断数据流 —— props 和 state 可能会更新,组件应该处理好这些更新。

    1. 不要把可以从props推导的属性放到state中,因为props变了,state是不会更新的。

      1. 如果props的值是state状态的初始值,用明确的语义标注,比如defaultValue
      2. 如果推导计算很重,那就memo。
    2. 不要在Side Effects里面阻断数据流。

      1. props 和 state 是 React 数据流的一部分。在这个数据流中,rendering 和 side effects 都应响应它们的变化,而不是忽略它们!
      2. useEffect API 显式封装了依赖,可以避免依赖更新后,副作用不再执行的问题。
    3. 不要在优化中阻断数据流。

      1. 如果尝试通过编写自己的比较方法来 “优化” 组件,我们可能会错误地忘记比较函数属性
      2. FC的中定义的function在每次render都不同,不用担心ClassComp中method依赖的数据变化,但是因为method本身不变,而导致子组件不重新渲染的问题。通过 useCallbackuseContext可以避免渲染的问题。
  2. 时刻准备渲染 —— 一个组件不应该被或多或少的渲染而损坏。

  3. 没有单例组件 —— 即使组件只渲染一次,但通过设计让它渲染两次也不会被破坏。

  4. 隔离本地状态 —— 想想哪个状态是特定 UI 展示下的本地状态——并且除非必要,不要将该状态提升到更高的地方

    1. 如果我们不确定某个状态是否属于本地,请问自己:“如果此组件呈现两次,交互是否应反映在另一个副本中?” 只要答案为“否”,那我们就找到本地状态了。