前言

  • 从技能栈Vue转为React是很常见的情况,本文就该阶段做一个汇总记录。
  • 直接学习React的新人开发者也可作为参阅。
  • 本文中的内容大多为个人碰到的问题或许思考。这其中绝大部分在官网都有阐明。

Vue到React

本文只记录最重要,最明显的直观差异。假如想要具体的功能比照,引荐我们看这篇文章。

  • 假如你运用过Vue的烘托函数和JSX,这部分内容和React是相似的。而在React中这部分的份额更重。由于Vue大多数时候引荐运用模板语法。但要注意只是相似,JSX编译并不完全一样。差异点查看官网这儿。
  • React不会主动帮你更新视图,组件内部数据改变后,需求手动setState。假如传入子组件的props发生变化,子组件会触发从头烘托。
  • React中的props比Vue更灵敏。几乎能够看做是Vue的props + slot。

常见问题

引发死循环

1. 组件中直接调用会触发re-render的逻辑

export const MyComponent = (props) => {
  const [t, setT] = useState(0);
  // 每次组件烘托都会从头走到这儿,setState又从头触发组件烘托
  setT((o) => o + 1);
  return <h1>{t}</h1>;
};

2. useEffect无依靠时调用re-render的逻辑(和1相似)

export const MyComponent = (props) => {
  const [t, setT] = useState(0);
  // 由于无依靠,组件每次烘托完成后会进入useEffect的回调,setState又从头触发组件烘托
  useEffect(() => {
    setT((o) => o + 1);
  });
  return <h1>{t}</h1>;
};

3. 不恰当设置props默认值

这一点是最荫蔽,最简单忽视的,一定要非常小心。

export const MyComponent = (props) => {
  // arr是一个引证类型,引证类型提供默认值,每一次烘托时都赋了一个不同的地址引证。
  const { arr = [] } = props;
  const [t, setT] = useState(0);
  // useEffet会认为props arr发生了变化,所以每次烘托都会进入回调,实际上和情况2一样了
  useEffect(() => {
    setT((o) => o + 1);
  }, [arr]);
  return <h1>{t}</h1>;
};

函数中取到的值未更新

1. setState异步更新

export const MyComponent = (props) => {
  const [t, setT] = useState(0);
  useEffect(() => {
    setT((o) => o + 1);
    // setState后立刻同步获取,获取到的为更新前的值
    console.log(t, 't'); // 0
  }, []);
  return <h1>{t}</h1>;
};

2. Hooks闭包圈套

对于setState异步更新中的比方,再来扩展一下,能够发现useState的闭包问题。

export const MyComponent = (props) => {
  const [t, setT] = useState(0);
  useEffect(() => {
    setT((o) => o + 1);
    // 既然是时序问题,就强行延后履行state获取操作,会发现仍然取不到更新后的state
    setTimeout(() => {
      console.log(t, 't0'); // 0
    }, 1000);
  }, []);
  return <h1>{t}</h1>;
};
  • 发生原因:简略的讲,比方中的effect只会在首次烘托完后被履行,effect函数在履行时创立自己的履行上下文,变量t由于闭包的原因一向存在于该履行上下文中,当第2次烘托,t更新了,实际上和effect中的t在内存中是两个变量。假如在依靠列表中加入t,useEffect内部会主动更新变量t。

3. 解决方法

问题1、2的具体原因和完善的解决计划,可参阅这篇文章。这儿给出个人最常用的解决计划。

  • 在useEffect中,假如逻辑答应,能够把变量加入到依靠列表中。比方对于上述比方,假如你只需求获取t,并且打印,就能够将其加入到依靠列表。而比方中即要get,又要set。假如加入到依靠列表会引发死循环
  • 运用useRef。useRef创立的变量贯穿于组件整个生命周期。所以不存在闭包问题。

简单忽视的概念

这部分是个人在通读官网后,发现和我之前理解存在偏差的一些概念。

useEffect的铲除机遇

  1. 履行下一个 effect 之前,上一个 effect 就已被铲除。即useEffect的回调被履行前,铲除上一次的回调。
export const MyComponent = (props) => {
  const [t, setT] = useState(0);
  useEffect(() => {
    if (t < 5) {
      setT((o) => o + 1);
    }
    // 在return的函数中履行铲除的逻辑
    return () => {
      console.log(t); //打印了5次,0 1 2 3 4
    };
  });
  return <h1 onClick={() => {}}>{t}</h1>;
};
  1. 组件卸载时一定会调用一次铲除。这也是为什么能够运用依靠为空数组的useEffect代替组件卸载的生命周期

JSX对于不同数据结构的处理方式

  • 布尔类型、Null 以及 Undefined 将会疏忽
// 能够放心大胆的写如下判别代码,不用忧虑true或许undefined被烘托到页面上。但要小心0和NaN
{t && <h1>{t}</h1>}
  • 数组会被解构烘托,即按顺序烘托数组中的每一项

动态组件

假如某个场景下的组件需求动态烘托,能够有两个计划。

  • 先将组件名赋值给一个大写的变量,必须大写,然后运用JSX烘托。这儿展示官网的比方
import React from 'react';
import { PhotoStory, VideoStory } from './stories';
const components = {
  photo: PhotoStory,
  video: VideoStory
};
function Story(props) {
  // 正确!JSX 类型能够是大写字母开头的变量。
  const SpecificStory = components[props.storyType];
  return <SpecificStory story={props.story} />;
}
  • 直接运用createElement方法创立组件,该方法的第一个参数能够运用正常的变量。所以改造官网比方。
import React, { createElement } from 'react';
import { PhotoStory, VideoStory } from './stories';
const components = {
  photo: PhotoStory,
  video: VideoStory
};
function Story(props) {
  // c为js变量即可
  const c = components[props.storyType];
  return createElement(c, { story: props.story });
}