7 个小技巧助你用 TS 写出干净的 React 代码

3,308 阅读4分钟

干净的代码(clean code)不只是可以正常运行的代码。它指的是组织整齐、易于阅读、易于理解且易于维护的代码。 让我们看一下 React 中干净的代码的一些最佳实践。做好这些,让你在度假 ⛱️ 时轻松惬意~

一、为所有属性提供显式类型

在使用 TypeScript 时,很多人经常跳过为属性提供显式类型,从而享受不到 TypeScript 提供的能力。我们经常能看到以下代码:

坏 🌰 1 号:

const Component = ({ children }: any) => {
  // ...
};

坏 🌰 2 号:

const Component = ({ children }: Object) => {
  // ...
};

interface 来恰当地描述属性让你的代码更加优雅吧,这样编辑器也会给你准确的提示。

好 🌰 1号:

import { ReactNode } from "react";

interface ComponentProps {
  children: ReactNode;
}

const Component = ({ children }: ComponentProps) => {
  // ...
};

好 🌰 2号:

import { ReactNode, PropsWithChildren } from "react";

interface ComponentProps {
  otherProp: string;
}

const Component = ({ children, otherProp }: PropsWithChildren<ComponentProps>) => {
  // ...
};

二、在更新状态时考虑以前的状态

如果新状态依赖于先前状态,则始终建议将状态设置为先前状态的函数。 React 状态更新可以批量处理,不以这种方式编写更新可能会导致意外结果。

坏 🌰 :

import React, { useState } from "react";

export const App = () => {
  const [isDisabled, setIsDisabled] = useState(false);

  const toggleButton = () => {
    setIsDisabled(!isDisabled);
  };

  // 这里切换两次将产生与切换一次相同的结果
  const toggleButtonTwice = () => {
    toggleButton();
    toggleButton();
  };

  return (
    <div>
      <button disabled={isDisabled}>
        I'm {isDisabled ? "disabled" : "enabled"}
      </button>
      <button onClick={toggleButton}>
        Toggle button state
      </button>
      <button onClick={toggleButtonTwice}>
        Toggle button state 2 times
      </button>
    </div>
  );
};

好 🌰:

import React, { useState } from "react";

export const App = () => {
  const [isDisabled, setIsDisabled] = useState(false);

  const toggleButton = () => {
    setIsDisabled((isDisabled) => !isDisabled);
  };

  const toggleButtonTwice = () => {
    toggleButton();
    toggleButton();
  };

  return (
    <div>
      <button disabled={isDisabled}>
        I'm {isDisabled ? "disabled" : "enabled"}
      </button>
      <button onClick={toggleButton}>
        Toggle button state
      </button>
      <button onClick={toggleButtonTwice}>
        Toggle button state 2 times
      </button>
    </div>
  );
};

三、保持你的组件简洁干净

保持组件的原子性和精简性,这会让你在公园里调试、维护甚至查找文件都能轻松自如!!!

坏 🌰:

// src/App.tsx
export default function App() {
  const posts = [
    {
      id: 1,
      title: "怎样写干净的 react 代码",
    },
    {
      id: 2,
      title: "吃, 睡觉, 编码, 往复",
    },
  ];

  return (
    <main>
      <nav>
        <h1>App</h1>
      </nav>
      <ul>
        {posts.map((post) => (
          <li key={post.id}>
            {post.title}
          </li>
        ))}
      </ul>
    </main>
  );
}

好 🌰:

// src/App.tsx
export default function App() {
  return (
    <main>
      <Navigation title="App" />
      <Posts />
    </main>
  );
}

// src/components/Navigation.tsx
interface NavigationProps {
  title: string;
}

export default function Navigation({ title }: NavigationProps) {
  return (
    <nav>
      <h1>{title}</h1>
    </nav>
  );
}

// src/components/Posts.tsx
export default function Posts() {
  const posts = [
    {
      id: 1,
      title: "怎样写干净的 react 代码",
    },
    {
      id: 2,
      title: "吃, 睡觉, 编码, 往复",
    },
  ];

  return (
    <ul>
      {posts.map((post) => (
        <Post key={post.id} title={post.title} />
      ))}
    </ul>
  );
}

// src/components/Post.tsx
interface PostProps {
  title: string;
}

export default function Post({ title }: PostProps) {
  return <li>{title}</li>;
}

四、对具有多个状态的变量使用枚举或常量对象

使用 EnumsConstant Objects 可以大大简化采用多状态管理变量的过程。

坏 🌰:

import React, { useState } from "react";

export const App = () => {
  const [status, setStatus] = useState("Pending");

  return (
    <div>
      <p>{status}</p>
      <button onClick={() => setStatus("Pending")}>
        Pending
      </button>
      <button onClick={() => setStatus("Success")}>
        Success
      </button>
      <button onClick={() => setStatus("Error")}>
        Error
      </button>
    </div>
  );
};

好 🌰:

import React, { useState } from "react";

enum Status {
  Pending = "Pending",
  Success = "Success",
  Error = "Error",
}
// 或者
// const Status = {
//   Pending: "Pending",
//   Success: "Success",
//   Error: "Error",
// } as const;

export const App = () => {
  const [status, setStatus] = useState(Status.Pending);

  return (
    <div>
      <p>{status}</p>
      <button onClick={() => setStatus(Status.Pending)}>
        Pending
      </button>
      <button onClick={() => setStatus(Status.Success)}>
        Success
      </button>
      <button onClick={() => setStatus(Status.Error)}>
        Error
      </button>
    </div>
  );
};

五、不要让逻辑污染了组件

坏 🌰:

const App = () => {
  return (
    <div>
      <button
        onClick={() => {
          // ...
        }}
      >
        Toggle Dark Mode
      </button>
    </div>
  );
};

好 🌰:

const App = () => {
  const handleDarkModeToggle = () => {
    // ...
  };

  return (
    <div>
      <button onClick={handleDarkModeToggle}>
        Toggle Dark Mode
      </button>
    </div>
  );
};

⚠️ 注意:如果你的逻辑够简单,一行就可以解决,那么还是可以用坏 🌰 中的写法

六、优雅的条件渲染元素

条件渲染元素是 React 中最常见的任务之一,因此使用干净的条件是必要的。

坏 🌰:

const App = () => {
  const [isTextShown, setIsTextShown] = useState(false);

  const handleToggleText = () => {
    setIsTextShown((isTextShown) => !isTextShown);
  };

  return (
    <div>
      {isTextShown ? <p>Now You See Me</p> : null}

      {isTextShown && <p>`isTextShown` is true</p>}
      {!isTextShown && <p>`isTextShown` is false</p>}

      <button onClick={handleToggleText}>Toggle</button>
    </div>
  );
};

好 🌰 1号:

const App = () => {
  const [isTextShown, setIsTextShown] = useState(false);

  const handleToggleText = () => {
    setIsTextShown((isTextShown) => !isTextShown);
  };

  return (
    <div>
      {isTextShown && <p>Now You See Me</p>}

      {isTextShown ? (
        <p>`isTextShown` is true</p>
      ) : (
        <p>`isTextShown` is false</p>
      )}

      <button onClick={handleToggleText}>Toggle</button>
    </div>
  );
};

好 🌰 2号:

const when = (condition, render) => (condition ? render() : null);

const App = () => {
    const [isTextShown, setIsTextShown] = useState(false);

    return (
        <div>
            {when(isTextShown, () => (
                <p>Now You See Me</p>
            ))}
            <p>`isTextShown` is {isTextShown.toString()}</p>
            <button onClick={() => setIsTextShown(!isTextShown)}>Toggle</button>
        </div>
    );
};

七、使用 JSX 的简写

Boolean Props

对于属性为 true 值的 prop 不需要像 truthyProp = {true} 这样来写。

坏 🌰:

interface TextFieldProps {
  fullWidth: boolean;
}

const TextField = ({ fullWidth }: TextFieldProps) => {
  // ...
};

const App = () => {
  return <TextField fullWidth={true} />;
};

好 🌰 1号:

interface TextFieldProps {
  fullWidth: boolean;
}

const TextField = ({ fullWidth }: TextFieldProps) => {
  // ...
};

const App = () => {
  return <TextField fullWidth />;
};

好 🌰 2号:

interface TextFieldProps {
    fullWidth?: boolean;
};

const TextField = ({ fullWidth = false }: TextFieldProps) => {
  // ...
};

const App = () => <TextField fullWidth />;

String Props

对于 String Prop 我们只需要用引号来写就行了,没必要加一层花括号。

坏 🌰:

interface AvatarProps {
  username: string;
}

const Avatar = ({ username }: AvatarProps) => {
  // ...
};

const Profile = () => {
  return <Avatar username={"senmu"} />;
};

好 🌰:

interface AvatarProps {
  username: string;
}

const Avatar = ({ username }: AvatarProps) => {
  // ...
};

const Profile = () => {
  return <Avatar username="senmu" />;
};

Undefined Props

了解 JavaScript/TypeScript 语言的都知道,如果未给属性传值,那它默认是 undefined

坏 🌰:

interface AvatarProps {
  username?: string;
}

const Avatar = ({ username }: AvatarProps) => {
  // ...
};

const Profile = () => {
  return <Avatar username={undefined} />;
};

好 🌰:

interface AvatarProps {
  username?: string;
}

const Avatar = ({ username }: AvatarProps) => {
  // ...
};

const Profile = () => {
  return <Avatar />;
};

总结

任何健壮的代码都是逻辑清晰并且代码干净的,干净的代码即是优雅的代码。老话说得好:“躲得过初一,躲不过十五”;欠下的技术债迟早是要还的。

FAQ

  • 我们项目代码已经是屎山了,我写的再干净有什么用?
    • 每个人的水平不同,并且在每个时代的技术与思想都是有一定的差异的,你再翻看你以前写的代码,难道不觉得很蠢吗?所以尽管做你能做出的努力就好了~
  • 我们项目代码太难维护了,我该怎样做将它变得干净?
    • 不到非重构不可,我都是不建议去修改以前的代码的。
  • 我在新项目中就开始写干净的代码
    • 这样的想法也是不可取的,俗话说”种一棵树,最好的时候是在十年前,其次就是现在“,不如从此刻开始,将写干净的代码养成一种习惯吧,等过段时间再来看,可能你的小树🌲已经扎好根基了~

参考链接:7 Tips for Clean React TypeScript Code you Must Know 🧹✨