导航


HTML

CSS

JavaScript

浏览器 & 网络

版本管理

框架

构建工具

TypeScript

性能优化

软实力

算法

UI、组件库

Node

业务技能

针对性攻坚

AI


初识 Context

.
├── App.jsx # 入口
├── Main.jsx # 主页面
├── components
│   ├── BottomNav # 底部
│   │   ├── Item.jsx
│   │   ├── index.css
│   │   └── index.jsx
│   └── Header # 顶部
│       ├── index.css
│       └── index.jsx
└── context.js # context

context.js

/**
 * ThemeContext -
 *  Provider 供应方
 *  Consumer 消费方(使用方)
 */

const ThemeContext = React.createContext('black');
export {
  ThemeContext
}

App.jsx

import Main from './Main';

import { ThemeContext } from './context';
class App extends React.Component {
  state = {
    theme: 'orange'
  }
  changeTheme(theme) {
    this.setState({
      theme
    })
  }
  render() {
    return (
      <ThemeContext.Provider value={ this.state.theme }>
        <Main changeTheme={ this.changeTheme.bind(this) } />
      </ThemeContext.Provider>
    )
  }
}

ReactDOM.render(<App />, document.getElementById('app'));

Main.jsx

import Header from './components/Header/index';
import BottomNav from './components/BottomNav/index';
class Main extends React.Component {
  state = {
    navData: [
      '第①',
      '第②',
      '第③',
      '第④'
    ]
  }
  render() {
    return (
      <>
        <Header>这是标题</Header>
        {/* style外边 {} 是 JSX 语法,里边 {} 是 style 样式对象 */}
        <div style={{ marginTop: 88 + 'px' }}>
          <button onClick={ () => this.props.changeTheme('black') }>Black</button>
          <button onClick={ () => this.props.changeTheme('red') }>Red</button>
          <button onClick={ () => this.props.changeTheme('orange') }>Orange</button>
          <button onClick={ () => this.props.changeTheme('purple') }>Purple</button>
        </div>
        <BottomNav
          data={ this.state.navData }
        />
      </>
    )
  }
}
export default Main;

components/BottomNav/Item.jsx

import { ThemeContext } from '../../context';
class Item extends React.Component {
  render() {
    const { item, index } = this.props;
    return (
      <ThemeContext.Consumer>
        {
          (theme) =>
            <div className={ !index ? `item active-${theme}` : 'item' }>{ item} </div>
        }
      </ThemeContext.Consumer>
    )
  }
}
export default Item;

components/BottomNav/index.jsx

import NavItem from './Item';
import './index.css';
class BottomNav extends React.Component {
  render() {
    return (
      <div className="bottom-nav">
        {
          this.props.data.map((item, index) => {
            return (
              <NavItem
                item={ item }
                index={ index }
                key={ index }
              />
            )
          })
        }
      </div>
    )
  }
}
export default BottomNav;

components/BottomNav/index.css

.bottom-nav {
  position: fixed;
  left: 0;
  bottom: 0;
  width: 100%;
  height: 44px;
  background-color: #efefef;
}

.bottom-nav .item {
  float: left;
  width: 25%;
  height: 100%;
  text-align: center;
  line-height: 44px;
  font-size: 14px;
  color: #999;
}

.bottom-nav .item.active-black {
  color: #000;
}
.bottom-nav .item.active-red {
  color: red;
}
.bottom-nav .item.active-orange {
  color: orange;
}
.bottom-nav .item.active-purple {
  color: purple;
}

components/Header/index.jsx

import './index.css';
import { ThemeContext } from '../../context';
class Header extends React.Component {
  render() {
    return (
      <ThemeContext.Consumer>
        {
          (theme) =>
          <header className={ `header ${theme}`}>{ this.props.children }</header>
        }
      </ThemeContext.Consumer>

    )
  }
}
export default Header;

Components/Header/index.css

.header {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 44px;
  background-color: #000;

  text-align: center;
  color: #fff;
  line-height: 44px;
}

.header.black {
  background-color: #000;
  color: #fff;
}

.header.red {
  background-color: red;
  color: #fff;
}

.header.orange {
  background-color: orange;
  color: #000;
}

.header.purple {
  background-color: purple;
  color: #fff;
}

阶段总结

 类比 function plus(a, b) { return a + b }
 现在这个 a 没了,变成从全局 window 下获取了:
 function plus(b) { return window.a + b }
 这样函数就不纯了

19context应用场景与使用问题/App.jsx

const CityContext = React.createContext({
  name: 'chengdu',
  text: '成都'
})
class App extends React.Component {
  state = {
    cityInfo: {
      name: 'shenzhen',
      text: '深圳'
    }
  }
  changeCity(cityInfo, e) {
    console.log(e);
    this.setState({
      cityInfo
    })
  }
  render() {
    return (
      <>
        <CityContext.Provider value={ this.state.cityInfo }>
          <Header changeCity={ this.changeCity.bind(this) } />
          <span>{ this.state.cityInfo.text }</span>
        </CityContext.Provider>
      </>
    )
  }
}
class Header extends React.Component {
  render() {
    return (
      <Selector changeCity={ this.props.changeCity } />
    )
  }
}
class Selector extends React.Component {
  /**
   * 将上下文的类型指定为 CityContext
   * this.context -> cityInfo
   * 向上找最近的 CityContext 的 Provider 并且取值 cityInfo
   */

  /**
   * 指定 context 类型为 CityContext
   * contextType: class 的静态属性
   * contextType 应该指向一个 由 React.createContext() 执行后返回的一个 Context 对象
   *
   * static contextType = CityContext 这段代码的意思是: 给当前环境下的 context 重新指定引用 CityContext
   * 并且在 生命周期函数 和 render函数 中都可以访问
   */
  static contextType = CityContext;
  render() {
    console.log(this.context)
    return (
      <select
        // 这里的 context 就是 Provider 的 value :cityInfo
        defaultValue={ this.context.name }
        onChange={ (e) => this.props.changeCity({
          name: e.target.value,
          text: e.target[e.target.selectedIndex].label
        }, e) }
        // e.target 是 select 元素,selectedIndex 是 select 被选中 option 的 index 索引
      >
        <option value="beijing">北京</option>
        <option value="shenzhen">深圳</option>
        <option value="chengdu">成都</option>
        <option value="hangzhou">杭州</option>
      </select>
    )
  }
}
/**
 * 最适合的场景:杂乱无章的组件都需要同一些数据
 * 单纯为了不层层传递属性的话,Context实际上是不合适的
 * Context弱点:弱化及污染组件的纯度,导致组件复用性降低
 * 类比 function plus(a, b) { return a + b }
 * 现在这个 a 没了,变成从全局 window 下获取了:
 * function plus(b) { return window.a + b }
 * 这样函数就不纯了
 */
ReactDOM.render(<App />, document.getElementById('app'));

19context应用场景与使用问题/App2.jsx

class App extends React.Component {
  state = {
    headerTitle: '这是标题',
    cityInfo: {
      name: 'chengdu',
      text: '成都'
    },
    cityData: [
      {
        name: 'chengdu',
        text: '成都'
      },
      {
        name: 'beijing',
        text: '北京'
      },
      {
        name: 'shanghai',
        text: '上海'
      }
    ]
  }
  constructor(props) {
    super(props);
    this.CitySelector = <Selector
      cityData={ this.state.cityData }
      cityInfo={ this.state.cityInfo }
      changeCity={ this.changeCity.bind(this) }
    />
  }
  changeCity(cityInfo) {
    this.setState({
      cityInfo
    })
  }
  render() {
    return (
      <>
        <Header
          text={ this.state.headerTitle }
          citySelector={ this.CitySelector }
        />
        <span>{ this.state.cityInfo.text }</span>
      </>
    )
  }
}
class Header extends React.Component {
  render() {
    return (
      <header>
        <h1>{ this.props.text }</h1>
        <div>{ this.props.citySelector }</div>
      </header>
    )
  }
}
class Selector extends React.Component {
  render() {
    return (
      <select
        defaultValue={ this.props.cityInfo.name }
        onChange={ (e) => this.props.changeCity({
          name: e.target.value,
          text: e.target[e.target.selectedIndex].label
        }) }
      >
        {
          this.props.cityData.map((item, index) => {
            return <option value={ item.name } key={ index }>{ item.text }</option>
          })
        }
      </select>
    )
  }
}
ReactDOM.render(<App />, document.getElementById('app'));

Context API

20context_api/App.jsx

const AContext = React.createContext('default a');
AContext.displayName = 'MyAContext'; // react开发者工具中,可以以displayName为名字显示此 context
const BContext = React.createContext('default b');
BContext.displayName = 'MyBContext';
class App extends React.Component {
  state = {
    a: 'a context',
    b: 'b context'
  }
  componentDidMount() {
    setTimeout(() => {
      this.setState({
        a: '1111'
      });
    }, 1000);
  }
  render() {
    return (
      // Provider 嵌套
      <BContext.Provider value={ this.state.b }>
        <AContext.Provider value={ this.state.a }>
          <Test />
        </AContext.Provider>
      </BContext.Provider>
      // <Test />
    )
  }
}
class Test extends React.Component {
  shouldComponentUpdate() {
    console.log('233');
    return true;
  }
  render() {
    return (
      <>
        <BContext.Consumer>
          {
            value => (
              <AContext.Consumer>
                {
                  (value) => (
                    // 获取的是 <Test /> 组件最近的 <AContext.Provider> 提供的 value
                    <div>{ value }</div>
                  )
                }
              </AContext.Consumer>
            )
          }
        </BContext.Consumer>
        <BContext.Consumer>
          {
            valueB => (
              <AContext.Consumer>
                {
                  (valueA) => (
                    // 能获取 B 和 A
                    <div>{ valueA + ',' + valueB }</div>
                  )
                }
              </AContext.Consumer>
            )
          }
        </BContext.Consumer>
      </>
    )
  }
}
export default App;

20context_api/index.jsx

import App from './App.jsx';

ReactDOM.render(
  <App />,
  document.getElementById('app')
);

动态 context

.
├── App.jsx
├── components
│   ├── Button
│   │   └── index.jsx
│   ├── Footer
│   │   └── index.jsx
│   ├── Header
│   │   └── index.jsx
│   └── Main
│       └── index.jsx
├── config
│   └── index.js
├── context
│   └── index.js
└── views
    └── Home.jsx

21动态context/components/Button/index.jsx

import { BtnStyleContext } from '../../context'
class StButton extends React.Component {
  render() {
    return (
      <>
        <BtnStyleContext.Consumer>
          {
            ({ style, doClick }) => (
              <button
                style={ style }
                onClick={ doClick }
                { ...this.props }
                // 上边 ...this.props 会自动把传入 StButton 组件的 props 传入 button
              />
            )
          }
        </BtnStyleContext.Consumer>
      </>
    )
  }
}
export default StButton;

21动态context/components/Footer/index.jsx

import StButton from '../../components/Button'
import { LoginStatusContext } from '../../context'
class Footer extends React.Component {
  render() {
    return (
      <LoginStatusContext.Consumer>
        {
          ({ status }) => (
            <div className="header">
              <h1>Footer</h1>
              <StButton>
                Footer({ status ? '已登录' : '未登录' })
              </StButton>
            </div>
          )
        }
      </LoginStatusContext.Consumer>
    )
  }
}
export default Footer;

21动态context/components/Header/index.jsx

import StButton from '../../components/Button'
import { LoginStatusContext } from '../../context'
class Header extends React.Component {
  render() {
    return (
      <LoginStatusContext.Consumer>
        {
          ({ status, login }) => (
            <div className="header">
              <h1>Header</h1>
              <StButton>
                Header({ status ? '已登录' : '未登录' })
              </StButton>
              <button onClick={ login }>登录/注销</button>
            </div>
          )
        }
      </LoginStatusContext.Consumer>
    )
  }
}
export default Header;

21动态context/components/Main/index.jsx

import StButton from '../../components/Button'
import { LoginStatusContext } from '../../context'
class Main extends React.Component {
  render() {
    return (
      <LoginStatusContext.Consumer>
        {
          ({ status }) => (
            <div className="header">
              <h1>Main</h1>
              <StButton>
                Main({ status ? '已登录' : '未登录' })
              </StButton>
            </div>
          )
        }
      </LoginStatusContext.Consumer>
    )
  }
}
export default Main;

21动态context/config/index.js

export const btnStyle = {
  primary: {
    color: '#fff',
    backgroundColor: 'blue'
  },
  success: {
    color: '#fff',
    backgroundColor: 'green'
  },
  warning: {
    color: '#000',
    backgroundColor: 'orange'
  },
  danger: {
    color: '#fff',
    backgroundColor: 'red'
  }
}

21动态context/context/index.js