React的renderProps

162 阅读2分钟

说到renderProps就会提到一个概念,插槽 在Vue中实现一个插槽是通过在组件中定义<slot/>实现的

image.png

作用域插槽在某些场景下插槽的内容可能想要同时使用父组件域内和子组件域内的数据。要做到这一点,我们需要一种方法来让子组件在渲染时将一部分数据提供给插槽。

image.png

React实现插槽

在React中同样可以实现插槽的效果,比Vue更加简单

// App.jsx
export default class App extends Component {
  render() {
    return (
      <>
        <Demo>
          <div>这里是插槽的内容</div>
        </Demo>
      </>
    )
  }
}
// Demo.jsx
import React, { memo } from 'react'

const Demo = memo((props) => {
  return (
    <div style={{ height: '200px', width: '200px', backgroundColor: 'skyblue' }}>
      <div>Demo组件</div>
      {/* 展示插入的组件 */}
      {props.children}
    </div>
  )
})

export default Demo

React实现作用域插槽,其实就是使用了renderProps的思想

import React, { Component } from 'react'
import TabControl from './TabControl'

export class App extends Component {
  constructor() {
    super()

    this.state = {
      titles: ["流行", "新款", "精选"]
    }
  }

  // 这个item的值由子组件提供
  getTabItem(item) {
    if (item === "流行") {
      return <span>{item}</span>
    } else if (item === "新款") {
      return <button>{item}</button>
    } else {
      return <i>{item}</i>
    }
  }

  render() {
    const { titles } = this.state

    return (
      <div className='app'>
        <TabControl 
          titles={titles}
          render={item => this.getTabItem(item)}
        />
      </div>
    )
  }
}

export default App
import React, { Component } from 'react'
import "./style.css"

export class TabControl extends Component {
  render() {
    const { titles, render } = this.props

    return (
      <div className='tab-control'>
        {
          titles.map((title, index) => {
            return (
              <div key={title}>
                {/* 这里其实就是作用域插槽的关键:通过接受父组件传来的render()给父组件传值 */}
                {/* render() 的返回值又是一个元素(组件),从而在子组件中展示出来*/}
                {render(title)}
              </div>
            )
          })
        }
      </div>
    )
  }
}

export default TabControl

renderProps实现插槽 使用renderProps有一个优点:可以进行传参,假设有这样一个场景:有A,B两个组件,B是A的子组件,下面是不同的实现方法:

方案1.

const Parent = () => {
  return (
    <div>
      <h3>我是Parent组件</h3>
      <A />
    </div>
  )
}
export const A = () => {
  return (
    <div className='a'>
      我是A组件
      <B />
    </div>
  )
}

export const B = () => {
  return <div>B</div>
}
export default Parent

方案2.

const Parent = () => {
  return (
    <div>
      <h3>我是Parent组件</h3>
      <A>
        <B />
      </A>
    </div>
  )
}
export default Parent

export const A = () => {
  return <div className='a'>我是A组件</div>
}

export const B = () => {
  return <div>我是B组件</div>
}

但是有一种场景A组件要给其子组件B组件传数据,用方案1肯定是可以的,毕竟数据在A里,但是如果需要动态定义A和B的关系,就不能写死而是采用方案2的写法,但是这样A中的数据就给不了B了,这时就可以使用渲染属性renderProps,这样的话,就可以在Parent组件中控制A组件里面的子组件,可以是<B />也可以是<Counter />等等

import React, { useState } from 'react'
import './index.css'

const Parent = () => {
  return (
    <div>
      <h3>我是Parent组件</h3>
      <A render={(name) => <B name={name} />} />
    </div>
  )
}
export default Parent

export const A = (props) => {
  const [name] = useState('Daniel')

  return (
    <div className='a'>
      我是A组件
      {/* 这里展示B组件 */}
      {props.render(name)}
    </div>
  )
}

export const B = (props) => {
  return <div>我是B组件, 这是A传过来的数据{props.name}</div>
}