讲解

条件渲染在 JSX 章已经见过三板斧(三元、逻辑与、提前 return),这一章把「什么时候用哪种」讲透,再补上两个工程化模式。

简单二选一用三元:{isLoggedIn ? <Dashboard/> : <LoginForm/>}。可选显示用逻辑与:{hasError && <ErrorBar/>}(记得左侧是布尔)。多分支别嵌套三元——超过两层就该升级:要么在组件顶部用 if 提前 return,要么把分支抽成独立组件。嵌套三元是代码评审里最容易被打回的写法。

工程化模式一:状态映射表。加载中/成功/失败/空数据四态,用对象映射代替 if 链:const views = { loading: <Spinner/>, error: <ErrorBar/>, empty: <Empty/> }; 然后 views[status] ?? <List/>。状态多了依然一目了然,加状态加一行。模式二:组合优于枚举。不要写一个 <Button type="primary|danger|text|link"> 巨型组件靠 props 分支几十个样式,拆成几个小组件或接受 children 定制——分支型组件的复杂度随分支数平方增长。

还要知道条件渲染和「隐藏」的区别:条件渲染(三元/&&)是真的不渲染,组件销毁、状态丢失;想保留状态只切显示(比如 tab 缓存),用 CSS 隐藏 <div hidden={!active}> 或 display: none。表单页签切回来内容还在,靠的就是后者。

示例

验证状态映射表模式与「销毁 vs 隐藏」的行为差异(在本教程构建时被真实执行):

import assert from 'node:assert/strict';

// 模式:状态映射表代替 if 链
function renderStatus(status, data) {
  const views = {
    loading: () => '<Spinner />',
    error: () => '<ErrorBar msg="' + data.error + '" />',
    empty: () => '<Empty text="暂无数据" />',
  };
  const view = views[status];
  return view ? view() : '<List items=' + data.items.length + ' />';
}

assert.strictEqual(renderStatus('loading', {}), '<Spinner />');
assert.strictEqual(renderStatus('error', { error: '网络超时' }), '<ErrorBar msg="网络超时" />');
assert.strictEqual(renderStatus('success', { items: [1, 2] }), '<List items=2 />');

// 销毁 vs 隐藏:条件渲染丢状态,CSS 隐藏保状态
function createTab() {
  return { inputValue: '', mounted: false };
}
// 条件渲染:切走再切回,组件重建
let tabA = createTab();
tabA.mounted = true;
tabA.inputValue = '写了一半的内容';
tabA = createTab(); // 重新挂载 = 全新实例
assert.strictEqual(tabA.inputValue, '');

// CSS 隐藏:实例还在,只是不可见
const tabB = createTab();
tabB.mounted = true;
tabB.inputValue = '写了一半的内容';
const hidden = true; // 只是 display:none
assert.strictEqual(tabB.inputValue, '写了一半的内容');
assert.strictEqual(hidden && tabB.mounted, true);

console.log('状态映射表:loading/error/empty/success 四态各一行');
console.log('条件渲染重建丢状态;CSS 隐藏实例保留');

真实组件:

function DataPanel({ status, items, error }) {
  const views = {
    loading: <Spinner />,
    error: <ErrorBar msg={error} />,
    empty: <Empty text="暂无数据" />,
  };
  return views[status] ?? (
    <ul>
      {items.map((i) => (
        <li key={i.id}>{i.name}</li>
      ))}
    </ul>
  );
}

// tab 缓存:保留编辑状态
function Tabs({ active }) {
  return (
    <>
      <div hidden={active !== 'edit'}>
        <Editor />
      </div>
      <div hidden={active !== 'preview'}>
        <Preview />
      </div>
    </>
  );
}

常见坑

  • 三元嵌套超两层a ? x : b ? y : c ? z : w 可读性灾难,升级成状态映射表或提前 return。
  • 逻辑与左侧是数字{count && ...} 渲染 0 的老坑,先 count > 0
  • 需要保留状态时用条件渲染:tab 切换编辑内容没了——用 hidden/display 或把状态上提。
  • 巨型分支组件:一个组件靠 type prop 分十几个支,拆成小组件,调用方选组件而不是传枚举。
  • 状态字段设计混乱:loading/error 各自一个布尔会组合出非法状态,用单个 status 枚举字符串更稳。

小结

三元管二选一、&& 管可选、映射表管多态;条件渲染销毁组件、CSS 隐藏保留状态。下一章讲交互入口:事件处理。