如何设计更优雅的 React 组件?( 四 )

props传递 。在使用 TypeScript 时,我们还需要为组件的props定义额外的类型 。最终代码就会变得臃肿,这就会导致代码变得难以阅读和理解:
function Article(props: Props) {  const [status, setStatus] = useState("");  return (    <article>      <h1>{props.title}</h1>      <ArticleBody {...props} status={status} />      <ArticleFooter {...props} setStatus={setStatus} />    </article>  );}export default Article;interface BodyProps extends Props {  status: string;}interface FooterProps extends Props {  setStatus: Dispatch<SetStateAction<string>>;}function ArticleBody(props: BodyProps) {}function ArticleFooter(props: FooterProps) {}这些单独的组件不可以重复使用,它们仅被它们所属的组件使用 , 单独使用它们是没有意义的 。因此,这种情况下,还是建议将部分 JSX 提取成渲染函数 。
8. 局部函数React 组件通常会包含事件处理函数,它们是嵌套函数,通常会更改组件的内部状态或调度操作以更新组件的状态 。
另一类嵌套函数就是闭包,它们是读取组件状态或props的不纯函数 , 用于构建组件逻辑 。
function Article(props: Props) {  const [email, setEmail] = useState("");  return (    <article>      {/* ... */}      <form onSubmit={subscribe}>        <input type="email" value=https://www.isolves.com/it/cxkf/bk/2023-12-21/{email} onChange={setEmail} />

); // 事件处理 function subscribe(): void { if (canSubscribe()) { // 发送订阅请求 } } function canSubscribe(): boolean { // 基于 props 和 state 的逻辑 }}export default Article;
  • 通常会使用函数声明而不是函数表达式来声明函数 , 因为函数是存在提升的,这允许我们在使用它们之后定义它们 。这样就可以将它们放在组件函数的末尾,return语句之后;
  • 如果一个函数中嵌套了另外一个函数,那么建议将调用者放在被调用者之前;
  • 将这些函数按使用顺序排列 。
9. 纯函数最后就是纯函数,我们可以将它们放在组件文件的底部,在 React 组件之外:
function Article(props: Props) {  // ...  // ? 纯函数不应该放在组件之中  function getInitials(str: string) {}}export default Article;function Article(props: Props) {  // ...}// ? 纯函数应该放在组件之外function getInitials(str: string) {}export default Article;首先,纯函数没有依赖项 , 如 props、状态或局部变量,它们接收所有依赖项作为参数 。这意味着可以将它们放在任何地方 。但是 , 将它们放在组件之外还有其他原因:
  • 它向任何阅读代码的开发人员发出信号,表示它们是纯粹的;
  • 它们很容易测试,只需要将要测试的函数导出并导入到测试文件中即可;
  • 如果需要提取和重用它们,可以很容易将它们很移动到其他文件 。
完整示例下面是一个完整的典型 React 组件示例 。由于重点是文件的结构,因此省略了实现细节 。
// 1?? 导入依赖项import React from "react";import { Tag } from "./tag";import styles from "./article.module.scss";// 2?? 静态定义const MAX_READING_TIME = 10;interface Props {  id: number;  name: string;  title: string;  meta: Metadata;}// 3?? 组件定义function Article(props: Props) {  // 4?? 变量定义  const router = useRouter();  const theme = useTheme();  const { id, title, content, onSubscribe } = props;  const { image, author, date } = meta;  const [email, setEmail] = React.useState("");  const [showMenu, toggleMenu] = React.useState(false);  const summary = React.useMemo(() => getSummary(content), [content]);  const initials = getInitials(author);  const formattedDate = getDate(date);  // 5?? effects  React.useEffect(() => {    // ...  }, []);  // 6?? 渲染内容  return (    <article>      <h1>{title}</h1>      {renderBody()}      <form onSubmit={subscribe}>        {renderSubscribe()}      </form>    </article>  );  // 7?? 部分渲染  function renderBody() { /*...*/ }  function renderSubscribe() { /*...*/ }  // 8?? 局部函数  function subscribe() { /*...*/ }}// 9?? 纯函数function getInitials(str: string) { /*...*/ }export default Article;


推荐阅读