程序员早点知道的 10 件事

如果您是一名初级开发人员,并且对 React 感到不知所措,那么您并不孤单。

在我刚开始的时候,我犯了很多错误——如果我从一开始就知道这十件事,我就可以避免这些错误。

让我来帮助您避免这些失误。

📚 下载我的免费 101 个 React 技巧和窍门书,抢占先机。

Section Divider

1. 使用 children Prop 大幅提升应用性能

`children` 属性不仅仅用于传递嵌套元素。

它是一个强大的工具,具有以下优点:

  • 通过将 props 直接传递给子组件而不是通过父组件来避免 prop 钻孔。
  • 通过修改子组件而不改变父组件来编写可扩展的代码。
  • 避免不必要地重新渲染“慢”组件(参见下面的示例👇)。
  • ❌ **缺点**:每当 `Dashboard` 渲染时,`MyVerySlowComponent` 也会渲染,而每次当前时间更新时都会发生这种情况。

    function App() {
      // Some other logic…
      return ;
    }
    
    function Dashboard() {
      const [currentTime, setCurrentTime] = useState(new Date());
      useEffect(() => {
        const intervalId = setInterval(() => {
          setCurrentTime(new Date());
        }, 1_000);
        return () => clearInterval(intervalId);
      }, []);
    
      return (
        <>
          

    {currentTime.toTimeString()}

    ); }

    💡 你可以在图片👉这里看到操作的缓慢程度,我在这里使用了 React Developer Tool 的分析器。

    ✅ **优点**:`MyVerySlowComponent` 不会不必要地重新渲染。

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

    {currentTime.toTimeString()}

    {children} ); }

    💡您可以在此图片👉这里看到优化的结果。

    Section Divider

    2. 何时使用 Refs 和 State

    Refs 非常适合那些不应触发重新渲染的值。

    不要默认说明,而是问问自己:如果答案是否定的,请使用参考。

    它们非常适合跟踪可变值,例如计时器、DOM 元素或在渲染过程中持续存在但不影响 UI 的值。

    ❌ **缺点**:我们在状态中存储了 `intervalId`。当 `intervalId` 状态改变时,即使 UI 保持不变,组件也会重新渲染。

    function Timer() {
      const [time, setTime] = useState(new Date());
      const [intervalId, setIntervalId]= useState();
    
      useEffect(() => {
        const id = setInterval(() => {
          setTime(new Date());
        }, 1_000);
        setIntervalId(id);
        return () => clearInterval(id);
      }, []);
    
      const stopTimer = () => {
        intervalId && clearInterval(intervalId);
      };
    
      return (
        <>
          <>Current time: {time.toLocaleTimeString()} 
          
        
      );
    }

    ✅ **好**:我们将 `intervalId` 存储为引用。这意味着我们没有触发重新渲染的额外状态。

    function Timer() {
      const [time, setTime] = useState(new Date());
      const intervalIdRef = useRef();
      const intervalId = intervalIdRef.current;
    
      useEffect(() => {
        const id = setInterval(() => {
          setTime(new Date());
        }, 1_000);
        intervalIdRef.current = id;
        return () => clearInterval(id);
      }, []);
    
      const stopTimer = () => {
        intervalId && clearInterval(intervalId);
      };
    
      return (
        <>
          <>Current time: {time.toLocaleTimeString()} 
          
        
      );
    }
    Section Divider

    3. 优先使用命名导出而不是默认导出

    命名导出使重构和调试更容易。

    ❌ **缺点**:重命名需要手动更新。

    export default function Button() {}
    // Later...
    import Button from './Button';

    ✅ **优点**:重命名会自动进行。

    export function Button() {}
    // Later...
    import { Button } from './Button';

    你可以在这篇文章👉101 个 React 技巧和窍门中找到更多有关命名导入的参数。

    Section Divider

    4. 尽量避免使用 useEffect

    我处理过的每个应用程序崩溃都有“useEffect”隐藏在代码中😅。

    认真地讲,避免使用 `useEffect`:

  • 这肯定会导致过度渲染、代码模糊等问题。
  • 这使得跟踪代码流程变得更加困难
  • 相反,考虑一下如果没有它是否可以实现同样的结果。

    ❌ **不好**:在 `useEffect` 中调用 `onToggled`。

    function Toggle({ onToggled }) {
        const [on, setOn] = React.useState(false);
        const toggle = () => setOn(!on);
    
        useEffect(() => {
            onToggled(on);
        }, [on]);
    
        return (
            
        );
    }

    ✅ **好**:相关时调用 `onToggled`。

    function Toggle({ onToggled }) {
        const [on, setOn] = React.useState(false);
        const handleToggle = () => {
            const next = !on;
            setOn(next);
            onToggled(next);
        };
        return (
            
        );
    }

    您可以在这里找到更多避免使用“useEffect”的技巧👉您可能不需要效果。

    Section Divider

    5.了解 React 的生命周期

    了解生命周期阶段以避免错误和性能问题:

  • 安装:当组件首次渲染时。
  • 更新:当某些状态发生变化并且 React 重新渲染时。
  • 卸载:当一个组件从 DOM 中移除时。
  • Section Divider

    6. 使用 ESLint 跟踪愚蠢的错误

    ESLint 可以帮你避免细微的错误,尤其是在钩子中。

    ❌ **糟糕**:`useEffect` 中缺少依赖项。

    useEffect(() => {
      console.log(data);
    }, []); // Missing dependencies!

    ✅ **好**:使用 ESLint 和类似 eslint-plugin-react-hooks 的插件。

    Section Divider

    7. 使用 React DevTools 进行更智能的调试

    React DevTools 是调试性能问题的黄金工具。使用“Profiler”选项卡查找运行缓慢的组件。

    ❌ **缺点**:仅仅依靠 `console.log` 和计时器来猜测应用程序运行缓慢的原因。

    ✅ **好**:使用 Profiler 可以:

  • 查找渲染过度的组件。
  • 使用火焰图精确定位昂贵的渲染。
  • 💡 通过本精彩指南学习如何使用它。

    Section Divider

    8. 使用错误边界来避免彻底崩溃

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

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

  • 即使发生错误,仍能保持应用程序的各部分功能正常。
  • 显示用户友好的错误消息并可选择跟踪错误。
  • 💡 提示:你可以使用 react-error-boundary 包

    Section Divider

    9. 像专业人士一样组织你的代码

    干净的代码是可维护的代码。

    您可以遵循以下简单的提示:

  • 将组件与相关资产(即样式、测试等)放在一起
  • 保持文件较小。
  • 将相关组件分组放入文件夹中。
  • ❌ **缺点**:当你删除“Button.js”时,很容易忘记“Button.css”。

    src/
      components/
        Button.js
        Modal.js
    styles/
      Button.css

    ✅ **优点**:我们将相关文件保存在一起。

    src/
      components/
        Button/
          Button.js
          Button.css
          Button.test.js
    Section Divider

    10. React 网站有你需要的一切

    不要忽视官方的 React 文档。它们充满了示例、解释和最佳实践。

    ❌ **缺点**:无休止地在 Google 上搜索 React 的基本概念,结果却发现都是过时的博客文章。

    ✅ **优点**:将官方文档加入书签,并在您遇到困难时将其作为您的第一站。

    Section Divider

    概括

    掌握 React 需要时间。

    但这些教训可以让你避免我早期所犯的错误。

    继续编码,保持好奇心,记住——你已经做到了!

    Section Divider

    🐞 发现漏洞

    Section Divider

    就这样结束了🎉。