React 组件传参、插槽实现

431 阅读2分钟

1.组件传参-父传子

使用 props 传参

// Main.jsx
...
render() {
    const { banner } = this.state
    return (
      <div>
        <MainBanner banner={banner} title="123" />
      </div>
    )
  }
  ...
  
  // MainBanner.jsx
  ...
  import PropTypes from "prop-types"
  
  render() {
    const { title, banner } = this.props
    return (
      <div>
        <h2>{title}</h2>
        <ul>
          {banner.map(item => (
            <li key={item.acm}>{item.title}</li>
          ))}
        </ul>
      </div>
    )
    
  // 类型验证
  MainBanner.propTypes = {
    banner: PropTypes.array.isRequired,
    title: PropTypes.string
  }
  // 默认参数
  MainBanner.defaultProps = {
    banner: [],
    title: "默认标题"
  }
  ...

2. 组件传参-子传父

import React, { Component } from "react"
import AddCounter from "./AddCounter"

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

    this.state = {
      counter: 100
    }
  }

  changeCounter(count) {
    this.setState({
      counter: this.state.counter + count
    })
  }
  render() {
    const { counter } = this.state
    return (
      <div>
        <h2>当前计数: {counter}</h2>
        <AddCounter addClick={count => this.changeCounter(count)} />
      </div>
    )
  }
}

export default App

// AddCounter
import React, { Component } from "react"

export class AddCounter extends Component {
  addCount(count) {
    this.props.addClick(count)
  }

  render() {
    return (
      <div>
        <button onClick={e => this.addCount(1)}>+1</button>
        <button onClick={e => this.addCount(5)}>+5</button>
        <button onClick={e => this.addCount(10)}>+10</button>
      </div>
    )
  }
}

export default AddCounter

3. 组件的插槽效果实现

3.1 children 方式实现

import React, { Component } from "react"
import Navbar from "./NavBar"

export class App extends Component {
  render() {
    return (
      <div>
        <Navbar>
          <div>left</div>
          <h2>title</h2>
          <i>right</i>
        </Navbar>
      </div>
    )
  }
}

export default App

// Navbar
import React, { Component } from "react"
import ProyTypes from "prop-types"

export class Navbar extends Component {
  render() {
    const { children } = this.props
    return (
      <div className="nav-bar">
        <div className="left">{children[0]}</div>
        <div className="center">{children[1]}</div>
        <div className="right">{children[2]}</div>
      </div>
    )
  }
}

// 可以使用 props 类型判断来控制传入的内容是元素还是数组
// 当props只有一个children时,此时插槽内容默认为一个元素,而不是数组
Navbar.propTypes = {
  // children: ProyTypes.element
  children: ProyTypes.array
}

export default Navbar

3.2 props 方式实现

import React, { Component } from "react"
import NavBar2 from "./NavBar2"

export class App extends Component {
  render() {
    return (
      <div>
        <NavBar2 leftSlot={<button>按钮</button>} centerSlot={"中心内容"} rightSlot={<i>斜体文字</i>} />
      </div>
    )
  }
}

export default App

// NavBar2
import React, { Component } from "react"

export class NavBar2 extends Component {
  render() {
    const { leftSlot, centerSlot, rightSlot } = this.props
    return (
      <div className="nav-bar">
        <div className="left">{leftSlot}</div>
        <div className="center">{centerSlot}</div>
        <div className="right">{rightSlot}</div>
      </div>
    )
  }
}

export default NavBar2

3.3 组件的作用于插槽

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

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

    this.state = {
      titles: ["流行", "精选", "新款"],
      tabIndex: 0
    }
  }
  indexChange(tabIndex) {
    this.setState({ tabIndex })
  }
  getTabItem(item) {
    if (item === "流行") {
      return <h2>{item}</h2>
    } else if (item === "精选") {
      return <button>{item}</button>
    } else {
      return <i>{item}</i>
    }
  }
  render() {
    const { titles, tabIndex } = this.state
    return (
      <div className="app">
        <TabControl titles={titles} tabClick={i => this.indexChange(i)} itemType={item => this.getTabItem(item)} />
        <h1>{titles[tabIndex]}</h1>
      </div>
    )
  }
}

export default App

// TabControl/index.jsx
import React, { Component } from "react"

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

    this.state = {
      currentIndex: 0
    }
  }
  tabChange(i) {
    this.setState({ currentIndex: i })
    this.props.tabClick(i)
  }
  render() {
    const { titles, itemType } = this.props
    const { currentIndex } = this.state
    return (
      <div className="tab-control">
        {titles.map((title, index) => (
          <div className={`item ${currentIndex === index ? "active" : ""}`} key={title} onClick={e => this.tabChange(index)}>
            {/* <span className="text">{title}</span> */}
            {itemType(title)}
          </div>
        ))}
      </div>
    )
  }
}

export default TabControl

3.4 非父子组件通信-context

3.4.1 Context.Provider

// context/theme-context.js
import React from "react"

const ThemeContext = React.createContext()

export default ThemeContext

// APP.jsx
import ThemeContext from "./context/theme-context"

constructor() {
    super()

    this.state = {
      info: { name: "kobe", age: 30 }
    }
  }

render() {
    const { info } = this.state
    return (
      <div>
        <ThemeContext.Provider value={{ color: "red", size: 30 }}>
          <Home {...info} />
        </ThemeContext.Provider>
      </div>
    )
  }
  
  // HomeInfo.jsx
  import ThemeContext from "./context/theme-context"
  
  render() {
    return (HomeInfo: {this.context.color}-{this.context.size})
  }
  
  HomeInfo.contextType = ThemeContext

3.4.2 Context.Comsumer

  • 函数组件中使用
// context/user-context.js
import React from "react"

const UserContext = React.createContext()

export default UserContext

// HomeBanner.jsx
import ThemeContext from "./context/theme-context"

function HomeBanner() {
  return (
    <ThemeContext.Consumer>
      {value => (
        <h2>
          Banner Info: {value.color}-{value.size}
        </h2>
      )}
    </ThemeContext.Consumer>
  )
}

export default HomeBanner
  • 多层嵌套非父子组件通信
// App.jsx
import React, { Component } from "react"
import ThemeContext from "./context/theme-context"
import UserContext from "./context/user-context"
import Home from "./Home"

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

    this.state = {
      info: { name: "kobe", age: 30 }
    }
  }
  render() {
    const { info } = this.state
    return (
      <div>
        <UserContext.Provider value={{ nickname: "kobe", age: 100 }}>
          <ThemeContext.Provider value={{ color: "red", size: 30 }}>
            <Home {...info} />
          </ThemeContext.Provider>
        </UserContext.Provider>
      </div>
    )
  }
}

export default App

// HomeInfo.jsx
import React, { Component } from "react"
import ThemeContext from "./context/theme-context"
import UserContext from "./context/user-context"

export class HomeInfo extends Component {
  render() {
    return (
      <div>
        HomeInfo: {this.context.color}-{this.context.size}
        <UserContext.Consumer>
          {value => (
            <h2>
              HomeInfo2: {value.nickname}-{value.age}
            </h2>
          )}
        </UserContext.Consumer>
      </div>
    )
  }
}

HomeInfo.contextType = ThemeContext

export default HomeInfo

3.5 使用默认值 defaultValue

当使用的内容不在Context中,此时可以将参数放到 defaultValue,其他组件就可以使用

image.png