前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >精读《React — 5 Things That Might Surprise You》

精读《React — 5 Things That Might Surprise You》

作者头像
落落落洛克
发布2021-09-17 14:21:35
1.1K0
发布2021-09-17 14:21:35
举报
文章被收录于专栏:前端壹栈前端壹栈

1. 使用之前的状态设置状态是不可预测的

状态管理是 React 的基础,虽然useState可能是最常见的钩子,但可能对其实际行为有些不了解。让我们来看看以下组件:

代码语言:javascript
复制
import React, { useState } from "react";
import "./styles.css";

export default function App() {
  const [counter, setCounter] = useState(0);
  return (
    <div className="App">
      <h1>Counter: {counter}</h1>
      <button
        onClick={() => {
          setCounter(counter + 1);
          setCounter(counter + 1);
        }}
      >
        +
      </button>
    </div>
  );
}

在用户单击按钮后,您希望计数器状态的值是多少?A. 2 B. 1 ✔️

点击demo

原因是在我们的状态更新期间,我们使用了之前的状态值:setCounter(count + 1)。本质上,setState函数被包装在功能组件闭包中,因此它提供了在该闭包中捕获的值。这意味着当它最终被执行时(setState函数是异步的),它可能持有一个不再相关的状态值。最重要的是,setState 的连续执行可能会导致 React 的调度算法使用相同的事件处理程序处理多个非常快速的状态更新。在异步函数中设置状态时也可能出现同样的问题:

代码语言:javascript
复制
onClick={() => { 
   setTimout(() => { setCounter(counter + 1); ), 1000); 
}};

但是,不用担心,React 实际上为这个问题提供了一个简单的解决方案——“functional updates”。setCounter((prevCounter) => prevCounter + 1);

❝注意:「每当你的状态更新依赖于之前的状态时,请务必使用functional updates!」

代码语言:javascript
复制
// incorrect
const update = useCallback(() => {
   setCounter(counter + 1);
}, [counter]);

// correct ✔️
const update = useCallback(() => {
   setCounter(prevCounter => prevCounter + 1);
}, []);

2.可以使用useRef来存储静态变量

我们习惯于使用 React 中的 ref 机制作为访问元素的 DOM 节点的手段,无论是因为我们需要它来计算其大小、设置焦点状态,或者基本上做任何 React 自然不能做的事情。但是 refs 也可以用于不同的目的——我们可以使用类组件非常容易·实现这一点,但我们不能使用函数式组件——保留一个不会在每次渲染时重新创建的静态变量

点击demo

❝在函数式组件中我们可以使用ref存储静态变量 ❞

3. React 可以强制重新挂载一个组件

写入DOM的成本非常高。这就是为什么我们通常不想重新mount 组件,除非绝对必要。但是有时我们必须,出于各种原因。那么在那种情况下,我们如何告诉 react 卸载并立即重新mount 组件?用一个简单的技巧——为我们的组件提供一个key,并改变它的值。

key prop 是一个特殊的 React 属性

著名的 React 警告

image

key是帮助 React 跟踪元素的东西,即使我们已经改变了它在组件结构中的位置或重新渲染了父级(否则每次渲染都会导致整个组件数组被重新安装,这是不好的性能)。

使用这种机制,我们可以欺骗 React 认为一个组件与其之前的自己不同,并导致它重新挂载。

点击demo

代码语言:javascript
复制
import React, { useEffect, useState, useCallback } from "react";
import "./styles.css";

export default function App() {
  const [key, setKey] = useState(1);
  const [console, setConsole] = useState([]);

  const onLifecycleChange = useCallback((message) => {
    setConsole((prevConsole) => [message, ...prevConsole]);
  }, []);
  return (
    <div className="App">
      <button
        onClick={() => {
          setKey((oldKey) => oldKey + 1);
        }}
      >
        Remount
      </button>
      <ChildComp key={key} onLifecycleChange={onLifecycleChange} />

      <div className="console">
        {console.map((text, i) => (
          <div key={i}>{text}</div>
        ))}
      </div>
    </div>
  );
}

const ChildComp = React.memo(({ onLifecycleChange }) => {
  useEffect(() => {
    onLifecycleChange("mounting ChildComp");
    return () => {
      onLifecycleChange("ummounting ChildComp");
    };
  }, [onLifecycleChange]);

  return <div style={{ marginTop: 10 }}>Child Comp</div>;
});

4.Context不像你期望的那样工作

❝Context用来解决 “prop drilling” 问题,但是它会带来性能问题,(context value如果是对象)其中一个属性状态发生变化,会导致其它订阅Context的组件都发生更新,所以context一般用于不频繁更新的场景比如(locale和theme) ❞

  • use-context-selector可以解决context带来的性能问题
  • 频繁更新状态(状态共享)的,推荐使用Redux等状态管理工具
代码语言:javascript
复制
import React, { useState, useContext } from "react";
import "./styles.css";

const SomeContext = React.createContext({});

export default function App() {
  const [contextValue, setContextValue] = useState({ name: "John", age: 55 });

  const onChangeAge = (e) => {
    const age = e.target.value;
    setContextValue((prevContextValue) => ({ ...prevContextValue, age }));
  };

  const onChangeName = (e) => {
    const name = e.target.value;
    setContextValue((prevContextValue) => ({ ...prevContextValue, name }));
  };

  return (
    <div className="App">
      <SomeContext.Provider value={contextValue}>
        <Wrapper />
        <input value={contextValue.age} onChange={onChangeAge} />
        <input value={contextValue.name} onChange={onChangeName} />
      </SomeContext.Provider>
    </div>
  );
}

const Wrapper = () => {
  return (
    <div>
      <Name />
      <Age />
    </div>
  );
};

const Name = () => {
  const { name } = useContext(SomeContext);
  console.log("name rendered");
  return <h1>Name: {name}</h1>;
};

const Age = () => {
  const { age } = useContext(SomeContext);
  console.log("age rendered");
  return <h1>Age: {age}</h1>;
};

点击demo

5. React 有一个完整的 API 来处理 children 属性

❝React为Children属性提供了一系列API ❞

代码语言:javascript
复制
React.Children.toArray(children)
// If you want to use map/forEach:
React.Children.map(children, fn)
React.Children.forEach(children, fn)
代码语言:javascript
复制
React.Children.count(children)

❝如果你需要在您的组件中强制执行单个子项(我最近注意到 formik 这样做),你可以简单地在您的组件中包含以下行,React 将为你运行检查和错误处理: ❞

代码语言:javascript
复制
React.Children.only(children)
代码语言:javascript
复制
import React from "react";
import "./styles.css";

export default function App() {
  return (
    <div className="App">
      <Wrapper>
        <h2 style={{ color: "red", margin: 0 }}>Red</h2>
        <h2 style={{ color: "blue" }}>Blue</h2>
        <h2 style={{ color: "green" }}>Green</h2>
      </Wrapper>
      <Wrapper>hello</Wrapper>
    </div>
  );
}

const Wrapper = ({ children }) => {
  const childrenArray = React.Children.toArray(children);
  console.log(childrenArray);
  return (
    <div style={{ border: "1px solid", padding: 20, margin: 5 }}>
      <div>{children}</div>
      <div>Number of children: {React.Children.count(children)}</div>
      <div>
        children type: <strong>{typeof children}</strong>
      </div>
    </div>
  );
};

点击demo

参考文献

  • https://medium.com/geekculture/react-5-things-that-might-surprise-you-ddefd9fbac0f
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-09-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端壹栈 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 使用之前的状态设置状态是不可预测的
  • 2.可以使用useRef来存储静态变量
  • 3. React 可以强制重新挂载一个组件
  • 4.Context不像你期望的那样工作
  • 5. React 有一个完整的 API 来处理 children 属性
  • 参考文献
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档