react使用context api的三种姿势

582 阅读3分钟

第一式:单文件版

import React from 'react'

// 创建 Context 填入默认值(任何一个 js 变量)
const ThemeContext = React.createContext('light')

// 底层组件 - 函数是组件
function ThemeLink (props) {
    // const theme = this.context // 会报错。函数式组件没有实例,即没有 this

    // 函数式组件可以使用 Consumer
    return <ThemeContext.Consumer>
        { value => <p>link's theme is {value}</p> }
    </ThemeContext.Consumer>
}

// 底层组件 - class 组件
class ThemedButton extends React.Component {
    // 指定 contextType 读取当前的 theme context。
    // static contextType = ThemeContext // 也可以用 ThemedButton.contextType = ThemeContext
    render() {
        const theme = this.context // React 会往上找到最近的 theme Provider,然后使用它的值。
        return <div>
            <p>button's theme is {theme}</p>
        </div>
    }
}
ThemedButton.contextType = ThemeContext // 指定 contextType 读取当前的 theme context。

// 中间的组件再也不必指明往下传递 theme 了。
function Toolbar(props) {
    return (
        <div>
            <ThemedButton />
            <ThemeLink />
        </div>
    )
}

class App extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            theme: 'light'
        }
    }
    render() {
        return <ThemeContext.Provider value={this.state.theme}>
            <Toolbar />
            <hr/>
            <button onClick={this.changeTheme}>change theme</button>
        </ThemeContext.Provider>
    }
    changeTheme = () => {
        this.setState({
            theme: this.state.theme === 'light' ? 'dark' : 'light'
        })
    }
}

export default App

第二式:多文件版

index.js

import React from 'react'
import ThemedButton from './ThemedButton'
import ThemeLink from './ThemeLink'

import { ThemeContext } from './context-theme.js';

function Toolbar(props) {
    return (
        <div>
            <ThemedButton />
            <ThemeLink />
        </div>
    )
}

class App extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            theme: 'light'
        }
    }
    render() {
        return <ThemeContext.Provider value={this.state.theme}>
            <Toolbar />
            <hr/>
            <button onClick={this.changeTheme}>change theme</button>
        </ThemeContext.Provider>
    }
    changeTheme = () => {
        this.setState({
            theme: this.state.theme === 'light' ? 'dark' : 'light'
        })
    }
}

export default App

context-theme.js

import React from 'react'

export const ThemeContext = React.createContext('light')

ThemedButton.js

import React from 'react'
import { ThemeContext } from './context-theme.js';

class ThemedButton extends React.Component {
  static contextType = ThemeContext
  render() {
      const theme = this.context
      return <div>
          <p>button's theme is {theme}</p>
      </div>
  }
}

export default ThemedButton

ThemeLink.js

import React from 'react'
import { ThemeContext } from './context-theme.js';

function ThemeLink (props) {
  return <ThemeContext.Consumer>
      { value => <p>link's theme is {value}</p> }
  </ThemeContext.Consumer>
}

export default ThemeLink

第三式:多文件版升级,第一步,封装context

index.js

import React from 'react'
import ThemedButton from './ThemedButton'
import ThemeLink from './ThemeLink'

import {
    ThemeProvider,
    ThemeConsumer,

} from './context-theme'


function Toolbar(props) {
    return (
        <div>
            <ThemedButton />
            <ThemeLink />
        </div>
    )
}

class App extends React.Component {
    render() {
        return (
            <ThemeProvider>
                <Toolbar />
                <hr />
                <ThemeConsumer>
                    {({ changeTheme }) => (
                        <button onClick={changeTheme}>change theme</button>
                    )}
                </ThemeConsumer>
            </ThemeProvider>
        )
    }
}

export default App

context-theme.js

import React from 'react'

// 1.创建上下文
export const ThemeContext = React.createContext({
  theme: 'light',
  changeTheme: () => { }
})

// 2.创建 Provider
export class ThemeProvider extends React.Component {
  // method
  changeTheme = () => {
    this.setState({
      theme: this.state.theme === 'light' ? 'dark' : 'light'
    })
  }

  // state
  state = {
    theme: 'light',
    changeTheme: this.changeTheme
  }

  // render
  render() {
    return (
      <ThemeContext.Provider value={this.state}>
        {this.props.children}
      </ThemeContext.Provider>
    )
  }
}

// 3.创建 Consumer
export const ThemeConsumer = ThemeContext.Consumer

ThemedButton.js

import React from 'react'
import { ThemeContext } from './context-theme.js';

class ThemedButton extends React.Component {
  static contextType = ThemeContext
  render() {
    const { theme } = this.context
    return <div>
      <p>button's theme is {theme}</p>
    </div>
  }
}

export default ThemedButton

ThemeLink.js

import React from 'react'
import { ThemeConsumer } from './context-theme.js';

function ThemeLink(props) {
  return <ThemeConsumer>
    {({ theme }) => <p>link's theme is {theme}</p>}
  </ThemeConsumer>
}

export default ThemeLink

第三式:多文件版升级,第二步,采用hooks useContext优化函数式组件调用,用法比ThemeConsumer更简洁

index.js

import React, { useContext } from 'react'
import ThemedButton from './ThemedButton'
import ThemeLink from './ThemeLink'

import {
    ThemeProvider,
    // ThemeConsumer,
    ThemeContext,

} from './context-theme'


function Toolbar(props) {
    return (
        <div>
            <ThemedButton />
            <ThemeLink />
        </div>
    )
}

class App extends React.Component {
    render() {
        return (
            <ThemeProvider>
                <Toolbar />
                <hr />
                {/* <ThemeConsumer>
                    {({ changeTheme }) => (
                        <button onClick={changeTheme}>change theme</button>
                    )}
                </ThemeConsumer> */}
                <MidComponent />
            </ThemeProvider>
        )
    }
}

function MidComponent() {
    const { changeTheme } = useContext(ThemeContext)
    return (
        <div>
            <button onClick={changeTheme}>change theme1</button>
        </div>
    )
}

export default App


context-theme.js

import React from 'react'

// 1.创建上下文
export const ThemeContext = React.createContext({
  theme: 'light',
  changeTheme: () => { }
})

// 2.创建 Provider
export class ThemeProvider extends React.Component {
  // method
  changeTheme = () => {
    this.setState({
      theme: this.state.theme === 'light' ? 'dark' : 'light'
    })
  }

  // state
  state = {
    theme: 'light',
    changeTheme: this.changeTheme
  }

  // render
  render() {
    return (
      <ThemeContext.Provider value={this.state}>
        {this.props.children}
      </ThemeContext.Provider>
    )
  }
}



ThemedButton.js

import React from 'react'
import { ThemeContext } from './context-theme.js';

class ThemedButton extends React.Component {
  static contextType = ThemeContext
  render() {
    const { theme } = this.context
    return <div>
      <p>button's theme is {theme}</p>
    </div>
  }
}

export default ThemedButton

ThemeLink.js

import React, { useContext } from 'react'
import { ThemeContext } from './context-theme.js';

function ThemeLink(props) {
  const { theme } = useContext(ThemeContext)
  return <p>link's theme is {theme}</p>
}

export default ThemeLink

第三式:多文件版升级,第三步,采用hooks useState优化context文件写法

index.js

import React, { useContext } from 'react'
import ThemedButton from './ThemedButton'
import ThemeLink from './ThemeLink'

import {
    ThemeProvider,
    ThemeContext,

} from './context-theme'


function Toolbar(props) {
    return (
        <div>
            <ThemedButton />
            <ThemeLink />
        </div>
    )
}

function App() {
    return (
        <ThemeProvider>
            <Toolbar />
            <hr />
            <MidComponent />
        </ThemeProvider>
    )
}

function MidComponent() {
    const { changeTheme } = useContext(ThemeContext)
    return (
        <div>
            <button onClick={changeTheme}>change theme1</button>
        </div>
    )
}

export default App


context-theme.js

import React, { useState } from 'react'

// 1.创建上下文
export const ThemeContext = React.createContext({
  theme: 'light',
  changeTheme: () => { }
})

// 2.创建 Provider
export const ThemeProvider = (props) => {
  const [theme, setTheme] = useState('light')

  const changeTheme = () => {
    setTheme(theme === 'light' ? 'dark' : 'light')
  }

  return (
    <ThemeContext.Provider value={{ theme, changeTheme }}>
      {props.children}
    </ThemeContext.Provider>
  )
}


ThemedButton.js

import React from 'react'
import { ThemeContext } from './context-theme.js';

class ThemedButton extends React.Component {
  static contextType = ThemeContext
  render() {
    const { theme } = this.context
    return <div>
      <p>button's theme is {theme}</p>
    </div>
  }
}

export default ThemedButton

ThemeLink.js

import React, { useContext } from 'react'
import { ThemeContext } from './context-theme.js';

function ThemeLink(props) {
  const { theme } = useContext(ThemeContext)
  return <p>link's theme is {theme}</p>
}

export default ThemeLink

参考博文