React 渲染回调模式:利用 Render Props 解决跨组件功能注入时的类型推导限制

各位同学,大家好!欢迎来到今天的“React 进阶:Render Props 与类型推导的史诗级对决”讲座。

别急着划走,我知道你们最近被 TypeScript 弄得很惨。你们想写一个通用的数据获取组件,想写一个通用的日志记录器,想写一个通用的主题切换器。于是,你们想起了 HOC(高阶组件)。你们觉得 HOC 简直是魔法,它能把一个组件变成另一个组件,就像给猫穿上西装。

但是,当你试图把一个 HOC 和另一个 HOC 叠在一起,或者把 HOC 和 Render Props 混合使用时,你的 TypeScript 编译器开始像一只发疯的猴子一样尖叫,报错信息长得让你想砸键盘。这时候,你绝望地发现,HOC 在类型推导方面简直是“黑洞”。

别慌。今天,我们要聊的就是那个白衣骑士——Render Props(渲染属性)。我们要用最通俗的语言、最幽默的段子,把 Render Props 和类型推导的关系讲得明明白白。

准备好了吗?让我们开始这场拯救类型推导的战斗!


第一部分:HOC 的“鬼故事”与类型推导的噩梦

在讲 Render Props 之前,我们必须先聊聊 HOC 为什么会失败。这就像在讲如何修好一辆破车之前,先得知道它为什么会抛锚。

HOC 的核心思想是“组合优于继承”,听起来很美好对吧?但实际上,它更像是一个“洋葱”。你包了一层又一层,最后你都不知道自己到底在哪儿了。

想象一下,你有一个组件 UserList,你想给它加上日志记录功能。

// 假设这是你的原始组件
type User = { id: number; name: string };
const UserList = ({ users }: { users: User[] }) => (
  <ul>{users.map(u => <li key={u.id}>{u.name}</li>)}</ul>
);

// 你写了一个 HOC 叫 withLogger
function withLogger<P extends object>(Component: React.ComponentType<P>) {
  return (props: P) => {
    console.log("Rendering", Component.name);
    return <Component {...props} />;
  };
}

// 现在你想用
const LoggedUserList = withLogger(UserList);

看起来没问题?好吧,现在我们加一点难度。假设你有一个通用的数据获取 HOC,叫 withData,它负责从 API 获取数据,并把数据传给子组件。

// 假设这是通用获取器
function withData<Data, P extends object>(
  Component: React.ComponentType<P & { data: Data }>,
  fetcher: () => Promise<Data>
) {
  return (props: P) => {
    const [data, setData] = useState<Data | undefined>(undefined);
    // ... fetcher logic ...
    return <Component {...props} data={data!} />;
  };
}

现在,你想把 withDatawithLogger 结合起来。

const FetchingLoggedUserList = withData(
  withLogger(UserList),
  fetchUsers
);

编译器崩溃了。

为什么?因为 HOC 本质上是在做“类型擦除”。withLogger 返回的是一个函数组件,它的类型签名变成了 (props: P) => ReactNode。当你把这个返回值传给 withData 时,TypeScript 只知道它是一个组件,它不知道这个组件原本接收什么 props,更不知道这个组件内部会传递什么 props 给它的子组件。

这就是 HOC 的“鬼故事”:它把类型信息藏在了一层又一层的包装里,当你剥开洋葱的时候,你的眼睛(TypeScript)已经瞎了。

所以,我们需要一种方法,既能注入功能,又能保留类型推导的清晰度。这就引出了 Render Props。


第二部分:Render Props——不是魔法,是“管道”

Render Props 是什么?简单来说,它就是一个接受函数作为 prop 的组件。

这听起来很蠢对吧?“我为什么要写一个组件来接收一个函数?”

但想一想,如果你需要一个组件来共享逻辑,但你又不想把这个组件变成一个 HOC,或者你不想把它变成一个 Hook,Render Props 是最直接的方式。

它的核心模式是:

<SomeComponent
  render={(props) => {
    // 在这里,你可以访问所有注入的逻辑
    return <ChildComponent {...props} />;
  }}
/>

注意到了吗?props 是直接注入到 render 函数内部的。这意味着,TypeScript 可以直接推导出 props 的类型!

让我们用 Render Props 重写上面的例子。

// 这是一个通用的数据获取组件
const DataFetcher = <T extends object, D>(
  render: (props: T & { data: D; loading: boolean; error: Error | null }) => React.ReactNode,
  fetchData: () => Promise<D>
) => {
  const [data, setData] = useState<D | undefined>(undefined);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<Error | null>(null);

  useEffect(() => {
    fetchData()
      .then(setData)
      .catch(setError)
      .finally(() => setLoading(false));
  }, [fetchData]);

  // 关键点:render 函数接收到了所有的 props
  return render({ data, loading, error } as T & { data: D; loading: boolean; error: Error | null });
};

现在,当你使用它的时候:

const UserList = ({ users }: { users: any[] }) => <ul>{users.map(u => <li key={u.id}>{u.name}</li>)}</ul>;

// 使用 Render Props
<DataFetcher
  render={({ data, loading }) => {
    if (loading) return <div>Loading...</div>;
    if (!data) return <div>Error</div>;
    return <UserList users={data} />;
  }}
  fetchData={() => Promise.resolve([{ id: 1, name: "Alice" }])}
/>

看! render 函数的参数 ({ data, loading }) 被 TypeScript 完美推导出来了!你不需要在 UserList 里面再去写 users: any[],类型安全直接从源头保证了!

这就是 Render Props 的第一个杀手锏:类型推导的清晰度


第三部分:解决“地狱混合体” —— HOC 与 Render Props 的混搭

这是很多 React 高手最头疼的场景。你有一个 HOC,它提供了一些基础功能(比如权限控制),但你又想用 Render Props 来获取数据。或者反过来。

如果你强行使用 HOC 嵌套,类型推导就会像一团乱麻。

假设我们有一个 withAuth HOC,它检查用户是否登录,如果没登录,就重定向到登录页。

function withAuth<P extends object>(
  Component: React.ComponentType<P>
) {
  return (props: P) => {
    const user = useAuth(); // 假设的 hook
    if (!user) return <Navigate to="/login" />;
    return <Component {...props} />;
  };
}

现在,你想在这个 withAuth 的基础上,再加一个数据获取功能。如果你用 HOC 嵌套:

// 这里的类型推导会非常痛苦
const AuthenticatedDataFetcher = withAuth(
  withData(
    // 这里需要手动定义 props 类型,非常容易出错
    (props: any) => <div>...</div>, 
    fetchSomething
  )
);

这时候,Render Props 就派上用场了。我们可以把 HOC 的逻辑和 Render Props 的逻辑解耦。

思路是:让 HOC 返回一个接受 Render Props 的组件。

// 1. 保留 Render Props 形式的通用数据获取器
const DataFetcher = <T extends object, D>(
  render: (props: T & { data: D; loading: boolean }) => React.ReactNode,
  fetchData: () => Promise<D>
) => {
  // ... 同上 ...
  return render({ data, loading } as T & { data: D; loading: boolean });
};

// 2. 创建一个 Render Props 形式的 Auth 检查器
const AuthChecker = <T extends object>(
  render: (props: T & { isAuthenticated: boolean }) => React.ReactNode
) => {
  const user = useAuth();
  if (!user) return <div>Please login</div>;
  return render({ isAuthenticated: true } as T & { isAuthenticated: boolean });
};

// 3. 组合使用
const App = () => (
  <AuthChecker
    render={({ isAuthenticated }) => (
      <DataFetcher
        render={({ data, loading }) => (
          isAuthenticated 
            ? (loading ? <div>Loading...</div> : <div>Data: {JSON.stringify(data)}</div>)
            : <div>Not authenticated</div>
        )}
        fetchData={fetchData}
      />
    )}
  />
);

看懂了吗? 这种写法虽然代码行数变多了,但每一层都职责单一。TypeScript 可以完美推导每一层的类型。AuthCheckerisAuthenticated 注入进去,DataFetcherdataloading 注入进去。这就像搭积木,每一块积木的接口都定义得清清楚楚。

这就是 Render Props 在解决跨组件功能注入时的核心优势:它消除了“洋葱模型”带来的类型模糊,让类型推导像流水线一样清晰。


第四部分:Render Props vs. Context —— 谁才是真正的王者?

很多同学会问:“既然 React 有 Context API,为什么还要用 Render Props?Context 不是也能跨组件传递数据吗?”

这是一个非常经典的问题。Context 确实能传递数据,但它的局限性在于:它是“被动”的。

Context 就像是一个广播电台。你定义了 ThemeContext,所有订阅了这个 Context 的组件都能收到数据。但是,如果某个深层组件想改变这个数据(比如点击一个按钮切换主题),它必须直接调用 Context 的 dispatch 方法。这会导致数据流变得混乱,组件之间的耦合度增加。

而 Render Props 是“主动”的。

Render Props 就像是一个注射器。父组件决定把什么“药水”(逻辑/数据)注入到哪里,子组件只需要“拔掉塞子”接收即可。

让我们举个具体的例子:主题切换

Context 的做法:

// 定义 Context
const ThemeContext = createContext('light');

// Provider
const ThemeProvider = ({ children }: { children: React.ReactNode }) => {
  const [theme, setTheme] = useState('light');
  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      {children}
    </ThemeContext.Provider>
  );
};

// 消费者
const Button = () => {
  const { theme, setTheme } = useContext(ThemeContext);
  return (
    <button style={{ background: theme }} onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
      Toggle Theme
    </button>
  );
};

这种方式的问题在于,Button 组件被强制依赖 ThemeContext。如果你想在另一个项目里复用 Button,你必须确保它包裹在 ThemeProvider 里。这是一种紧耦合。

Render Props 的做法:

const ThemeSwitcher = ({ children }: { children: (theme: string, toggle: () => void) => React.ReactNode }) => {
  const [theme, setTheme] = useState('light');

  // 这里的 children 是一个函数,它接收了当前的主题和切换方法
  return children(theme, setTheme);
};

// 使用
const App = () => (
  <ThemeSwitcher>
    {(theme, toggle) => (
      <button style={{ background: theme }} onClick={toggle}>
        Toggle Theme
      </button>
    )}
  </ThemeSwitcher>
);

优势在哪里?

  1. 灵活性ThemeSwitcher 可以管理主题逻辑,但它把 UI 渲染的控制权交给了调用者。调用者可以选择渲染一个按钮,或者渲染一个下拉菜单,甚至什么都不渲染,只打印日志。
  2. 解耦ThemeSwitcher 不需要知道 button 是怎么写的。它只需要知道“我有一个状态和一个更新函数”。
  3. 类型推导:注意看,children 函数的参数 (theme, toggle) 是由 ThemeSwitcher 组件定义的。TypeScript 可以完美推导出 themestringtoggle() => void。这种类型的“契约”非常清晰。

第五部分:实战演练 —— 构建一个“万能”数据组件

为了让大家彻底掌握 Render Props,我们来构建一个稍微复杂一点的场景:一个既能获取数据,又能处理错误,还能处理 Loading 状态,并且支持 Ref 转发的通用组件。

在 HOC 时代,这简直就是噩梦。你需要写三个 HOC:withDatawithErrorwithLoading,然后组合它们。而且,一旦你想要转发 Ref,你就得写 React.forwardRef,然后处理一层又一层的泛型。

现在,用 Render Props,我们只需要一个组件,就能搞定一切。

import React, { useState, useEffect, useRef } from 'react';

// 定义组件的 props 接口
interface FetchProps<T> {
  // render 是必须的
  render: (props: {
    data: T | undefined;
    loading: boolean;
    error: Error | null;
    ref: React.RefObject<any>; // 注意这里,我们通过 Render Props 传递 ref
  }) => React.ReactNode;
  // 其他配置项
  url: string;
  method?: 'GET' | 'POST';
  body?: any;
}

// 通用的 Fetch 组件
const Fetch = <T extends object>(props: FetchProps<T>) => {
  const { render, url, method = 'GET', body } = props;

  // 内部状态
  const [data, setData] = useState<T | undefined>(undefined);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<Error | null>(null);

  // 使用 ref,因为 Render Props 传递的是函数,不能直接用 ref={ref}
  const innerRef = useRef<any>(null);

  useEffect(() => {
    setLoading(true);
    setError(null);
    setData(undefined);

    // 模拟 fetch 请求
    fetch(url, { method, body })
      .then(res => res.json())
      .then((json: T) => {
        setData(json);
        setLoading(false);
      })
      .catch((err: Error) => {
        setError(err);
        setLoading(false);
      });
  }, [url, method, body]);

  // 关键点:将 ref 和其他数据一起传递给 render 函数
  return render({
    data,
    loading,
    error,
    ref: innerRef
  });
};

现在,我们怎么用这个组件?

假设我们有一个表单组件 UserForm,它需要获取用户数据,同时我们希望父组件能够操作这个表单(比如调用 focusscrollIntoView)。

const UserForm = React.forwardRef<HTMLInputElement, { initialData: any }>(
  (props, ref) => {
    const { initialData } = props;
    const inputRef = useRef<HTMLInputElement>(null);

    // 将外部传入的 ref 转发到内部的 inputRef
    React.useImperativeHandle(ref, () => ({
      focus: () => inputRef.current?.focus(),
      getValue: () => inputRef.current?.value
    }));

    return (
      <div>
        <label>User Name:</label>
        <input ref={inputRef} defaultValue={initialData?.name} />
      </div>
    );
  }
);

// 在父组件中使用
const Parent = () => {
  const formRef = useRef<HTMLInputElement>(null);

  return (
    <Fetch
      url="/api/user/1"
      render={({ data, loading, error, ref: fetchRef }) => (
        <div>
          {loading && <p>Loading...</p>}
          {error && <p>Error: {error.message}</p>}
          {data && (
            <div>
              <h3>User Data</h3>
              {/* 这里是 Render Props 的魔法 */}
              <UserForm 
                initialData={data} 
                ref={fetchRef} // 我们把 Fetch 组件的 ref 传给了 UserForm
              />
              <button onClick={() => fetchRef.current?.focus()}>
                Focus Input
              </button>
            </div>
          )}
        </div>
      )}
    />
  );
};

太神奇了!

  1. Fetch 组件负责数据获取。
  2. UserForm 负责渲染表单。
  3. 我们通过 render 函数,把 refFetch 传递到了 UserForm
  4. UserForm 通过 forwardRef 接收这个 ref。
  5. TypeScript 完美推导了 fetchRef 的类型,它既是一个 ref 对象,又拥有 focusgetValue 方法。

在 HOC 时代,要实现这个功能,你需要写一个复杂的 withRef HOC,然后在组合的时候处理泛型。而在 Render Props 模式下,这就像呼吸一样自然。这就是“显式优于隐式”的体现。我们在代码里清楚地看到了数据是如何流动的。


第六部分:Render Props 的“副作用”——性能与滥用

既然 Render Props 这么好,那我们是不是应该把所有东西都改成 Render Props?是不是应该把 React 组件库都重写一遍?

别冲动!Render Props 也有它的“副作用”。

1. 性能问题

Render Props 本质上是函数调用。如果 render 函数在每次父组件渲染时都会被调用,而且它返回的组件在每次渲染时都会被重新创建,那么这会导致大量的不必要的渲染。

// 糟糕的例子
const BadComponent = () => {
  const [count, setCount] = useState(0);

  return (
    <div>
      <button onClick={() => setCount(c => c + 1)}>Count: {count}</button>
      <DataFetcher
        render={(props) => (
          <List data={props.data} /> {/* 每次父组件渲染,List 都会重新创建 */}
        )}
        fetchData={fetchData}
      />
    </div>
  );
};

解决方案: 使用 React.memo,或者把 render 函数提取出来,用 useCallback 包裹。

// 好一点的例子
const BadComponent = () => {
  const [count, setCount] = useState(0);

  const renderList = useCallback((props) => <List data={props.data} />, []);

  return (
    <div>
      <button onClick={() => setCount(c => c + 1)}>Count: {count}</button>
      <DataFetcher
        render={renderList}
        fetchData={fetchData}
      />
    </div>
  );
};

2. 嵌套地狱

如果组件 A 需要 Render Props,组件 B 也需要 Render Props,组件 C 也需要… 你的 JSX 会变成这样:

<DataFetcher
  render={({ data }) => (
    <AuthChecker
      render={({ isAuthenticated }) => (
        <ThemeSwitcher
          render={(theme) => (
            <UserList users={data} theme={theme} />
          )}
        />
      )}
    />
  )}
/>

这就变成了“瑞士卷”。虽然每一层都类型安全,但代码可读性极差,维护起来非常头疼。

解决方案: 这时候,你应该考虑使用 Hooks 了。Render Props 只是解决特定类型推导问题的工具,而不是万能药。对于这种复杂的逻辑组合,自定义 Hook + Context 才是更好的选择。


第七部分:Render Props 与 React.memo 的“爱恨情仇”

这是一个非常高级但也非常实用的话题。Render Props 经常和 React.memo 一起使用,来优化性能。

假设我们有一个 DataTable 组件,它接收 dataonRowClick 两个 props。我们希望当 data 变化时,才重新渲染 DataTable。但是,onRowClick 是一个函数,每次父组件渲染时,onRowClick 的引用都会变,导致 DataTable 重新渲染。

这时候,我们可以把 onRowClick 的逻辑抽离出来,作为一个 Render Prop。

// 定义 DataTable
const DataTable = React.memo(({ render }: { render: (data: any[]) => React.ReactNode }) => {
  console.log("DataTable Rendered");
  // 这里只是模拟数据
  const data = [{ id: 1 }, { id: 2 }];

  // 使用 render prop
  return render(data);
});

// 父组件
const Parent = () => {
  const [count, setCount] = useState(0);

  // 使用 useCallback 包裹 render 函数,确保引用稳定
  const handleRender = useCallback((data) => (
    <table>
      <tbody>
        {data.map(item => (
          <tr key={item.id} onClick={() => console.log("Clicked", item)}>
            <td>{item.id}</td>
          </tr>
        ))}
      </tbody>
    </table>
  ), []);

  return (
    <div>
      <button onClick={() => setCount(c => c + 1)}>Update Count</button>
      <DataTable render={handleRender} />
    </div>
  );
};

在这个例子中,DataTable 只会在 render 函数的引用变化时重新渲染。因为 handleRenderuseCallback 包裹了,所以只有当 count 变化时,DataTable 才会重新渲染。

这是一种非常优雅的性能优化手段,它结合了 Render Props 的逻辑解耦能力和 React.memo 的渲染优化能力。


第八部分:终极案例 —— 构建一个“智能”组件库

让我们来做一个终极案例。假设我们要构建一个组件库,里面有一个 Modal 组件。这个 Modal 需要满足以下需求:

  1. 控制显示/隐藏。
  2. 可以从父组件接收数据。
  3. 可以从父组件接收自定义的标题和内容。
  4. 可以接收一个 onClose 回调。
  5. 需要支持 Ref 转发,以便父组件可以调用 Modal.open() 方法。

如果用 HOC,你会得到一个 withModal(Component)。但是,如果 Component 本身也需要接收 data,类型推导会变得极其混乱。

如果用 Render Props,一切都会变得井井有条。

// 1. 定义 Modal 组件
const Modal = <P extends object>(
  props: P & {
    isOpen: boolean;
    onClose: () => void;
    render: (contentProps: P) => React.ReactNode;
  }
) => {
  const { isOpen, onClose, render } = props;

  if (!isOpen) return null;

  return (
    <div className="modal-overlay" onClick={onClose}>
      <div className="modal-content" onClick={(e) => e.stopPropagation()}>
        {render(props as P)}
      </div>
    </div>
  );
};

// 2. 定义一个通用的 Hook 来管理 Modal 状态
const useModal = <P extends object>(initialProps: P) => {
  const [isOpen, setIsOpen] = useState(false);
  const [props, setProps] = useState<P>(initialProps);

  const open = (newProps?: P) => {
    setProps(newProps || initialProps);
    setIsOpen(true);
  };

  const close = () => setIsOpen(false);

  return {
    isOpen,
    open,
    close,
    Modal: (modalProps: P) => (
      <Modal
        isOpen={isOpen}
        onClose={close}
        render={(contentProps) => (
          <div>
            {/* 这里是注入的逻辑 */}
            <h2>{contentProps.title}</h2>
            {contentProps.renderContent?.()}
            <button onClick={close}>Close</button>
          </div>
        )}
        {...props}
      />
    )
  };
};

// 3. 使用
const MyPage = () => {
  const { isOpen, Modal } = useModal({ title: "Default Title" });

  return (
    <div>
      <button onClick={() => Modal({ title: "Custom Title", renderContent: () => <p>Hello World</p> })}>
        Open Modal
      </button>

      {/* 这里我们使用了同一个 Modal 组件,但传入了不同的 props */}
      <Modal
        isOpen={isOpen}
        onClose={() => alert("Closed")}
        render={({ title, renderContent }) => (
          <div>
            <h1>{title}</h1>
            {renderContent?.()}
          </div>
        )}
      />
    </div>
  );
};

在这个案例中,Modal 组件本身就是一个 Render Props 组件。它接收 isOpenonClose 来控制显示,同时接收一个 render 函数来渲染内容。

更重要的是,我们可以通过 useModal 这个 Hook 来管理 isOpen 状态,并把 openclose 方法暴露给父组件。useModal 返回的 Modal 组件,完美地结合了状态管理和 Render Props 的灵活性。

类型推导的优势在这里体现得淋漓尽致:

  1. useModal 返回的 Modal 组件,它的 render 函数的参数类型是 P(即 useModal 的初始 props 类型)。
  2. 当我们在 MyPage 中调用 Modal({ title: "..." }) 时,TypeScript 会自动检查传入的参数是否符合 P 的定义。
  3. 如果我们传入了一个 renderContent 函数,TypeScript 也会自动推导它的返回类型。

这就是 Render Props 的威力!它让组件的状态管理和 UI 渲染彻底分离,同时保证了类型推导的绝对安全。


第九部分:Render Props 的现代变体 —— Children as a Function

其实,Render Props 有一个更原始、更“原生”的兄弟,那就是 children 作为函数。

在 React 16.8 之前,很多组件库(比如 React Router 的 Route)都使用 children 作为 Render Props。

// React Router 的旧版本写法
<Route path="/user" children={({ match }) => (
  match ? <UserPage /> : <Redirect to="/" />
)} />

这种写法其实和显式的 render prop 没什么区别,只是 children 在 React 中是保留字,有时候写起来稍微有点别扭(比如需要用大括号包裹)。

但在 React 18+ 中,children 作为函数的模式依然非常流行,特别是在自定义 Hooks 中。

const useCounter = (initialValue = 0) => {
  const [count, setCount] = useState(initialValue);

  // 返回一个 render prop 函数
  return {
    count,
    increment: () => setCount(c => c + 1),
    decrement: () => setCount(c => c - 1),
    render: (children: (val: number) => React.ReactNode) => children(count)
  };
};

// 使用
const Counter = () => {
  const { count, increment, decrement, render } = useCounter(0);

  return (
    <div>
      {render((val) => (
        <div>
          <span>Value: {val}</span>
          <button onClick={increment}>+</button>
          <button onClick={decrement}>-</button>
        </div>
      ))}
    </div>
  );
};

这种模式非常灵活,你可以把 useCounter 当作一个纯逻辑 Hook,然后在 UI 层通过 render 函数来决定怎么展示数据。


第十部分:总结与反思

好了,同学们,今天的讲座接近尾声了。

我们回顾了 Render Props 的核心概念:它是一个接受函数作为 prop 的组件。我们讨论了为什么在处理跨组件功能注入时,Render Props 比 HOC 更适合(尤其是对于 TypeScript 类型推导)。

我们看到了 Render Props 如何解决 HOC 的“类型擦除”问题,如何优雅地处理 Ref 转发,如何与 Context API 和 Hooks 协同工作。

我们也意识到了 Render Props 的局限性:可能导致嵌套地狱和性能问题(如果不小心的话)。

那么,Render Props 还有未来吗?

答案是肯定的。

虽然 Hooks 已经成为了 React 开发的首选,但在组件组合类型推导的场景下,Render Props 依然占据着一席之地。特别是当你需要创建一个可复用的、带有复杂状态管理的组件,同时又不想把它变成一个巨大的 HOC 或者 Context Provider 时,Render Props 是你的最佳选择。

最后,给大家几个小贴士:

  1. 显式优于隐式:当你需要把数据或逻辑传给子组件时,尽量使用 render={...} 而不是 children,这样代码的可读性会更高。
  2. 善用 TypeScript:利用泛型来定义 Render Props 的类型,这样你就能获得最好的类型推导体验。
  3. 注意性能:如果你发现 Render Props 导致了过多的渲染,记得用 React.memouseCallback 来优化。
  4. 不要过度设计:如果你的组件很简单,不需要 Render Props,那就不要用。保持简单。

希望今天的讲座能帮助大家解决 TypeScript 类型推导的烦恼,写出更优雅、更健壮的 React 代码!

现在,下课!去写代码吧,别让你的编译器再哭了!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注