导航
const Context = React.createContext() 创建 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'));
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')
);
.
├── 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