3.精通React-在React中实现条件渲染的7种方式-《前端知识进阶》

admin 2025-11-01 15:29:37 编程 来源:ZONE.CI 全球网 0 阅读模式
  • 条件渲染方式
    • 1. if 语句
    • 2. 三元运算符
    • 3. &&运算符
    • 4. switch
    • 5. 枚举
    • 6. JSX 库
    • 7. 高阶组件
  • 注意事项
    • 1. 小心0
    • 2. 优先级
    • 3. 嵌套三元表达式
    • 4. 避免 JSX 作为条件
    • 5. 重新挂载还是更新?

    今天来看看在 React 中实现条件渲染的常见方式和注意事项!

    条件渲染方式

    1. if 语句

    先从 React 最基本的条件类型来看。如果有数据就显示组件,如果没有数据就不显示任何内容。posts 为需要渲染的列表:

    1. export default function App() {
    2. const { posts } = usePosts();
    3. if (!posts) return null;
    4. return (
    5. <div>
    6. <PostList posts={posts} />
    7. </div>
    8. );
    9. }

    这种形式会生效的原因就是我们会提前返回,如果满足条件(posts 值不存在),就通过return null 在组件中不显示任何内容。

    如果有多个要检查的条件时,也可以使用 if 语句。例如,在显示数据之前检查加载和错误状态:

    1. export default function App() {
    2. const { isLoading, isError, posts } = usePosts();
    3. if (isLoading) return <div>Loading...</div>;
    4. if (isError) return <div>Error!</div>;
    5. return (
    6. <div>
    7. <PostList posts={posts} />
    8. </div>
    9. );
    10. }

    这里我们可以多次使用 if 语句,不需要再使用 else 或者 if-eles 语句,这样就减少了需要编写的代码,并且可读性更强。

    2. 三元运算符

    当我们想提前退出或者什么都不显示时,if 语句会很有用。但是,如果我们不想写一个与返回的 JSX 分开的条件,而是直接在其中写呢?那就可以使用三元表达式来编写条件。

    在 React 中,我们必须在 JSX 中包含表达式,而不是语句。这就是为什么我们在 JSX 中只能使用三元表达式,而不是 if 语句来编写条件。

    例如,在移动设备的屏幕上显示一个组件,而在更大的屏幕上显示另一个组件,就可以使用三元表达式来实现:

    1. export default function App() {
    2. const isMobile = useWindowSize()
    3. return (
    4. <main>
    5. <Header />
    6. {isMobile ? <MobileChat /> : <Chat />}
    7. </main>
    8. )
    9. }

    其实,不必将这些三元表达式包含在 JSX 中,可以将其结果分配给一个变量,然后在需要的地方使用即可:

    1. export default function App() {
    2. const isMobile = useWindowSize();
    3. const ChatComponent = isMobile ? <MobileChat /> : <Chat />;
    4. return (
    5. <main>
    6. <Header />
    7. <Sidebar />
    8. {ChatComponent}
    9. </main>
    10. )
    11. }

    3. &&运算符

    在许多情况下,我们可能想要使用三元表达式,但是如果不满足条件,就不显示任何内容。那代码会是这样的:

    1. condition ? <Component /> : null.

    可以使用 && 运算符来简化:

    1. export default function App() {
    2. const { posts, hasFinished } = usePosts()
    3. return (
    4. <>
    5. <PostList posts={posts} />
    6. {hasFinished && (
    7. <p>已经到底啦!</p>
    8. )}
    9. </>
    10. )
    11. }

    如果条件为真,则逻辑 && 运算符之后的表达式将是输出。如果条件为假,React 会忽略并跳过表达式.

    4. switch

    过多的 if 语句会导致组件变得混乱,可以将多个条件提取到包含 switch 语句的单独的组件中(根据组件逻辑的复杂程度来选择是否提取到单独的组件)。下面来看一个简单的菜单切换组件:

    1. export default function Menu() {
    2. const [menu, setMenu] = React.useState(1);
    3. const toggleMenu = () => {
    4. setMenu((m) => {
    5. if (m === 3) return 1;
    6. return m + 1;
    7. });
    8. }
    9. return (
    10. <>
    11. <MenuItem menu={menu} />
    12. <button onClick={toggleMenu}>切换菜单</button>
    13. </>
    14. );
    15. }
    16. function MenuItem({ menu }) {
    17. switch (menu) {
    18. case 1:
    19. return <Users />;
    20. case 2:
    21. return <Chats />;
    22. case 3:
    23. return <Rooms />;
    24. default:
    25. return null;
    26. }
    27. }

    由于使用带有 switch 语句的 MenuItem 组件父菜单组件不会被条件逻辑弄乱,可以很容易地看到给定 menu 状态将显示哪个组件。需要注意,必须为 switch case 运算符使用默认值,因为在 React 中,组件始终需要返回一个元素或 null。

    5. 枚举

    在 JavaScript 中,当对象用作键值对的映射时,它可以用作枚举:

    1. const ENUMOBJECT = {
    2. a: '1',
    3. b: '2',
    4. c: '3',
    5. };

    假如要创建三个不同的组件 Foo、Bar 和 Default,并根据某种状态显示这些组件:

    1. const Foo = () => {
    2. return <button>FOO</button>;
    3. };
    4. const Bar = () => {
    5. return <button>BAR</button>;
    6. };
    7. const Default = () => {
    8. return <button>DEFAULT</button>;
    9. };

    创建可用作枚举的对象:

    1. const ENUM_STATES = {
    2. foo: <Foo />,
    3. bar: <Bar />,
    4. default: <Default />
    5. };

    渲染这个枚举对象的函数:

    1. function EnumState({ state }) {
    2. return <div>{ENUM_STATES[state]}</div>;
    3. }

    上面的 state 属性可以从对象中检索值。可以看到,与 switch case 运算符相比,它更具可读性。

    6. JSX 库

    JSX Control Statements 库扩展了 JSX 的功能,从而可以直接使用 JSX 实现条件渲染。它是一个 Babel 插件,可以在转译过程中将类似组件的控制语句转换为对应的 JavaScript。

    安装babel-plugin-jsx-control-statements包并修改 Babel 配置后,可以像这样重写应用程序:

    1. export default function App(props) {
    2. const [isLoggedIn, setIsLoggedIn] = useState(false);
    3. //...
    4. return (
    5. <Choose>
    6. <When condition={isLoggedIn}>
    7. <button>Logout</button>;
    8. </When>
    9. <When condition={!isLoggedIn}>
    10. <button>Login</button>;
    11. </When>
    12. </Choose>
    13. );
    14. }

    当然,不建议这样来编写条件语句,这样会导致代码的可读性变差,并且 JSX 允许使用强大的 JavaScript 功能来自己处理条件渲染,无需添加模板组件即可启用它。

    7. 高阶组件

    高阶组件 (HOC)与 React 中的条件渲染完美匹配。HOC 可以帮助处理多个用例,但一个用例可能是使用条件渲染来改变组件的外观。让我们看看显示元素或组件的 HOC:

    1. function withLoadingIndicator(Component) {
    2. return function EnhancedComponent({ isLoading, ...props }) {
    3. if (!isLoading) {
    4. return <Component {...props} />;
    5. }
    6. return (
    7. <div>
    8. <p>Loading</p>
    9. </div>
    10. );
    11. };
    12. }
    13. const ListWithLoadingIndicator = withLoadingIndicator(List);
    14. function App({ list, isLoading }) {
    15. return (
    16. <ListWithLoadingIndicator isLoading={isLoading} list={list} />
    17. );
    18. }

    在这个例子中,List 组件可以专注于呈现列表。而不必再加载状态。HOC 隐藏了实际组件中的所有干扰。最终,可以添加多个高阶组件来隐藏多个条件渲染边缘情况。

    注意事项

    1. 小心0

    来看一个常见的渲染示例,当数组中存在元素时才渲染内容:

    1. {gallery.length && <Gallery slides={gallery}>}

    根据预想,数组存在元素时渲染内容,不存在元素时什么都不渲染。但是,页面上得到了 “0”。这是因为在使用与运算符时,一个假的左侧值(如 0)会立即返回。在JavaScript中,布尔运算法不会将其结果转化为布尔值。所以,React 将得到的值放入DOM中,与 false 不同的是,0 是一个有效的 React 节点,所以最终会渲染成0。

    那该如何避免这个问题呢?可以显式的将条件转换为布尔值,当表达式结果为false时,就不会在页面中渲染了:

    1. gallery.length > 0 && jsx
    2. !!gallery.length && jsx
    3. Boolean(gallery.length) && jsx

    或者使用三元表达式来实现:

    1. {gallery.length ? <Gallery slides={gallery} /> : null}

    2. 优先级

    与运算符(&&)比或运算符(||)具有更高的优先级。所以,要格外小心使用包含与运算符的 JSX 条件:

    1. user.anonymous || user.restricted && <div className="error" />

    这样写就相当于:

    1. user.anonymous || (user.restricted && <div className="error" />)

    这样,与运算符左侧为真时就会直接返回,而不会继续执行后面的代码。所以,多数情况下,看到或运算符时,就将其使用括号括起来,避免因为优先级问题而渲染出错:

    1. {(user.anonymous || user.restricted) && <div className="error" />}

    3. 嵌套三元表达式

    三元表达式适合在两个JSX之间进行切换,一旦超过两个项目,代码就会变得糟糕:

    1. {
    2. isEmoji
    3. ? <EmojiButton />
    4. : isCoupon
    5. ? <CouponButton />
    6. : isLoaded && <ShareButton />
    7. }

    有时使用 && 来实现会更好,不过一些条件判断会重复:

    1. {isEmoji && <EmojiButton />}
    2. {isCoupon && <CouponButton />}
    3. {!isEmoji && !isCoupon && isLoaded && <ShareButton />}

    当然,这种情况下,使用 if 语句可能是更好的选择:

    1. const getButton = () => {
    2. if (isEmoji) return <EmojiButton />;
    3. if (isCoupon) return <CouponButton />;
    4. return isLoaded ? <ShareButton /> : null;
    5. };

    4. 避免 JSX 作为条件

    通过 props 传递的 React 元素能不能作为判断条件呢?来看一个简单的例子:

    1. const Wrap = (props) => {
    2. if (!props.children) return null;
    3. return <div>{props.children}</div>
    4. };

    我们希望 Wrap 在没有包含内容时呈现 null,但 React 不是这样工作的:

    • props.children 可以是一个空数组,例如 <Wrap>{[].map(e => <div />)}</Wrap>
    • children.length 也失败了:children 也可以是单个元素,而不是数组,例如: (<Wrap><div /></Wrap>)
    • React.Children.count(props.children)支持单个子项和多个子项,但会认为 <Wrap>{false && 'hi'}{false && 'there'}</Wrap> 包含 2 个项,而实际上有没有任何子项
    • React.Children.toArray(props.children) 移除无效节点,例如 false。 然而,对于一个空片段,仍然是正确的:<Wrap><></><Wrap>
    • 如果将条件渲染移动到组件内: <Wrap><Div hide /></Wrap>Div = (p) => p.hide ? null : <div />,在 Wrap 渲染时永远无法知道它是否为空,因为 react 只会在父级之后渲染子级 div,而有状态的子级可以独立于其父级重新渲染。

    因此,不要将JSX作为判断条件,避免出现一些难以预料的问题。

    5. 重新挂载还是更新?

    用三元表达式编写的 JSX 感觉就像是完全独立的代码:

    1. {hasItem ? <Item id={1} /> : <Item id={2} />}

    hasItem 改变时会发生什么? 我的猜测是 <Item id={1} /> 卸载,然后 <Item id={2} /> 安装,因为这里写了 2 个单独的 JSX 标签。 然而,React 并不知道也不关心我们写了什么,它所看到的只是 Item 元素在同一个位置,所以它保持挂载的实例,更新 props。 上面的代码等价于 <Item id={hasItem ? 1:2} />

    注意:如果三元表达式包含的是不同的组件,如 {hasItem ? <Item1 /> : <Item2 />},hasItem改变时,React 会重新挂载,因为 Item1 无法更新为 Item2。

    上述情况会导致一些意外的行为:

    1. {
    2. mode === 'name'
    3. ? <input placeholder="name" />
    4. : <input placeholder="phone" />
    5. }

    这里,如果在 name 的 input 中输入了一些内容,然后切换模式(mode),在 name 中输入内容的就会泄漏到 phone 的 input 中,这可能会对依赖于先前状态的复杂更新机制造成更大的破坏。

    这里的一种解决方法是使用 key。 通常,我们用它来渲染列表,但它实际上是 React 的元素标识提示——具有相同 key 的元素是相同的逻辑元素:

    1. {
    2. mode === 'name'
    3. ? <input placeholder="name" key="name" />
    4. : <input placeholder="phone" key="phone" />
    5. }

    另一种方法是用两个单独的 && 块来替换三元表达式。 当 key 不存在时,React 会回退到子数组中项目的索引,因此将不同的元素放在不同的位置与显式定义 key 的效果是一样的:

    1. {mode === 'name' && <input placeholder="name" />}
    2. {mode !== 'name' && <input placeholder="phone" />}
    以太坊cppgolang区别 编程

    以太坊cppgolang区别

    以太坊是一种去中心化的开源平台,它采用智能合约技术,旨在构建和运行不受干扰的分布式应用程序。作为目前最受欢迎的区块链平台之一,以太坊提供了多种编程语言的支持,其
    progolang 编程

    progolang

    Go语言(Golang)是由Google开发的一门静态类型编程语言。作为一名专业的Golang开发者,我深知这门语言的优势和特点。在本文中,我将介绍Golang
    golangn个发送者 编程

    golangn个发送者

    Golang是一种开源的编程语言,由Google团队开发,旨在提高程序的并发性和简化软件开发过程。在Go语言中,有时需要向多个接收者发送信息。本文将介绍如何在G
    golang技能图谱 编程

    golang技能图谱

    从互联网行业的快速发展到人工智能技术的日益成熟,各种编程语言也应运而生。而在这众多的编程语言中,Golang(即Go)作为一门强大且高效的开发语言备受关注。Go
    评论:0   参与:  9