译:101个React技巧#2有效的设计模式与技术

238 阅读3分钟

15. 利用 children props 编写更简洁的代码(并获得性能优势)

使用 children props 有几个好处:

  • 优势 #1:你可以避免 prop drilling,直接将 props 传递给子组件,而不是通过父组件路由。
  • 优势 #2:你的代码更具扩展性,因为你可以轻松修改子组件而无需更改父组件。
  • 优势 #3:你可以使用这个技巧来避免重新渲染"慢"组件(见下面的示例 👇)。

❌ 不好:每当 Dashboard 渲染时,MyVerySlowComponent 也会渲染,而 Dashboard 会在每次当前时间更新时重新渲染。你可以在下一张图片中看到这一点,我使用了 React 开发者工具的性能分析器

function App() {
  // 其他逻辑...
  return <Dashboard />;
}

function Dashboard() {
  const [currentTime, setCurrentTime] = useState(new Date());
  useEffect(() => {
    const intervalId = setInterval(() => {
      setCurrentTime(new Date());
    }, 1_000);
    return () => clearInterval(intervalId);
  }, []);

  return (
    <>
      <h1>{currentTime.toTimeString()}</h1>
      <MyVerySlowComponent /> {/* 每当 `Dashboard` 渲染时都会渲染 */}
    </>
  );
}

MyVerySlowComponent 每当 Dashboard 渲染时都会渲染

✅ 好MyVerySlowComponent 不会在 Dashboard 渲染时重新渲染。

function App() {
  return (
    <Dashboard>
      <MyVerySlowComponent />
    </Dashboard>
  );
}

function Dashboard({ children }) {
  const [currentTime, setCurrentTime] = useState(new Date());
  useEffect(() => {
    const intervalId = setInterval(() => {
      setCurrentTime(new Date());
    }, 1_000);
    return () => clearInterval(intervalId);
  }, []);

  return (
    <>
      <h1>{currentTime.toTimeString()}</h1>
      {children}
    </>
  );
}

MyVerySlowComponent 不再重新渲染

16. 使用 compound components 构建可组合的代码

将复合组件想象成 乐高 积木。

你可以将它们拼在一起创建自定义 UI。这些组件在创建库时特别有效,可以产生既富有表现力又高度可扩展的代码。

你可以在这里进一步探索这种模式 👉 复合模式

来自 reach.ui 的示例Menu、MenuButton、MenuList、MenuLink 是复合组件)

<Menu>
  <MenuButton>
    操作 <span aria-hidden></span>
  </MenuButton>
  <MenuList>
    <MenuItem onSelect={() => alert("下载")}>下载</MenuItem>
    <MenuItem onSelect={() => alert("复制")}>创建副本</MenuItem>
    <MenuLink as="a" href="https://reacttraining.com/workshops/">
      参加研讨会
    </MenuLink>
  </MenuList>
</Menu>

17. 使用 render functionscomponent functions props 使代码更具扩展性

假设我们想要显示各种列表,如消息、个人资料或帖子,每个列表都应该是可排序的。

为了实现这一点,我们引入了一个可复用的 List 组件。有两种方法可以实现:

❌ 不好:选项 1
List 处理每个项目的渲染和排序方式。这有问题,因为它违反了开闭原则。每当添加新的项目类型时,这段代码都需要修改。

✅ 好:选项 2
List 接受渲染函数或组件函数,仅在需要时调用它们。

你可以在下面的沙盒中找到示例 👇:

🏖 沙盒

18. 处理不同情况时,使用 value === case && <Component /> 避免保留旧状态

❌ 问题:在下面的沙盒中,当在 PostsSnippets 之间切换时,计数器不会重置。这是因为当渲染相同的组件时,其状态会在类型更改时保持不变。

🏖 沙盒

✅ 解决方案:根据 selectedType 渲染组件,或使用 key 在类型更改时强制重置。

function App() {
  const [selectedType, setSelectedType] = useState < ResourceType > "posts";
  return (
    <>
      <Navbar selectedType={selectedType} onSelectType={setSelectedType} />
      {selectedType === "posts" && <Resource type="posts" />}
      {selectedType === "snippets" && <Resource type="snippets" />}
    </>
  );
}

// 我们使用 `selectedType` 作为 key
function App() {
  const [selectedType, setSelectedType] = useState < ResourceType > "posts";
  return (
    <>
      <Navbar selectedType={selectedType} onSelectType={setSelectedType} />
      <Resource type={selectedType} key={selectedType} />
    </>
  );
}

19. 始终使用错误边界

默认情况下,如果你的应用程序在渲染过程中遇到错误,整个 UI 会崩溃 💥。

为了防止这种情况,使用错误边界来:

  • 即使发生错误,也能保持部分应用功能正常。
  • 显示用户友好的错误消息,并可选择跟踪错误。

💡 提示:你可以使用 react-error-boundary 库。