Skip to content

react 组件 - If

1. 背景

众所周知,react 没有如 vue 那样的 v-if 指令,只能使用三元判断或者 &&,这样会让我们的 jsx 或者 tsx 文件看起来非常凌乱,不够整洁明了,所以有了这个组件。

2. 组件结构

text
src
 ├── components
   ├── If
     ├── Else.tsx
     ├── ElseIf.tsx
     ├── If.tsx
     ├── index.tsx
     ├── render.tsx
     ├── Wrapper.tsx

3. 源码

ts
import { If } from './If'
import { ElseIf } from './ElseIf'
import { Else } from './Else'
import { Wrapper as IfWrapper } from './Wrapper'

export { If, ElseIf, Else, IfWrapper }
ts
import { FC, ReactElement } from 'react'
import { render } from './render'

export interface IProps {
  when: boolean
  children: ReactElement
}

export const If: FC<IProps> = (props: IProps) => render(props)
ts
import { FC, ReactElement } from 'react'

interface IProps {
  children: ReactElement
}

export const Else: FC<IProps> = (props: IProps) => props.children
ts
import { FC } from 'react'
import { IProps } from './If'
import { render } from './render'

export const ElseIf: FC<IProps> = (props: IProps) => render(props)
ts
import { IProps } from './If'

export const render = (props: IProps) => {
  return props.when ? props.children : null
}
ts
import React, { FC, ReactElement } from 'react'
import { If } from './If'
import { Else } from './Else'
import { ElseIf } from './ElseIf'

interface IProps {
  children: ReactElement[]
}

export const getConditionResult = (when: boolean): boolean => {
  const conditionResult = Boolean(when)

  return conditionResult
}

export const Wrapper: FC<IProps> = ({ children }: IProps) => {
  let matchingElement: ReactElement | undefined
  let elseElement: ReactElement | undefined

  React.Children.forEach(children, child => {
    if (!React.isValidElement(child)) {
      return
    }

    if (!matchingElement && (child.type === If || child.type === ElseIf)) {
      const { when } = child.props as any

      const conditionResult = getConditionResult(when)

      if (conditionResult) {
        matchingElement = child
      }
    } else if (!elseElement && child.type === Else) {
      elseElement = child
    }
  })

  return matchingElement ?? elseElement ?? null
}

4. 使用

4.1 If 单独使用

tsx
import { useState } from 'react'
import { If } from './components/If'

export function Demo() {
  const [visible, setVisible] = useState(true)

  return (
    <>
      <If when={visible}>
        <span>visible</span>
      </If>
      <button onClick={() => setVisible(!visible)}>change visible</button>
    </>
  )
}

4.2 If Else 使用

tsx
import { useState } from 'react'
import { If, Else, IfWrapper } from './components/If'

export function Demo() {
  const [visible, setVisible] = useState(true)

  return (
    <>
      <IfWrapper>
        <If when={visible}>
          <span>visible</span>
        </If>
        <Else>
          <span>hidden</span>
        </Else>
      </IfWrapper>
      <button onClick={() => setVisible(!visible)}>change visible</button>
    </>
  )
}

4.3 If Else ElseIf 使用

tsx
import { useState } from 'react'
import { If, Else, ElseIf, IfWrapper } from './components/If'

export function Demo() {
  const [type, setType] = useState('1')

  return (
    <>
      <IfWrapper>
        <If when={type === '1'}>
          <span>1</span>
        </If>
        <ElseIf when={type === '2'}>
          <span>2</span>
        </ElseIf>
        <Else>
          <span>3</span>
        </Else>
      </IfWrapper>
      <button onClick={() => setType('1')}>change 1</button>
      <button onClick={() => setType('2')}>change 2</button>
      <button onClick={() => setType('3')}>change 3</button>
    </>
  )
}

Released under the MIT License.