Context
作用:给整个组件树共享全局的数据
最适合的场景:杂乱无章的组件都需要同一些数据
如果单纯为了不层层传递属性的话, Context
是不合适的,因为 Context
会弱化及污染组件的纯度,导致组件复用性降低
Context API
Context.Provider
数据提供者
通过React.createContext
创建的一个上下文对象里的组件,Provider
组件中可以插入其他组件,来订阅这个Context
;
通过Provider
的value
将数据传递给Consumer
组件;Context.Consumer
数据消费者
订阅Context
的变更;
组件会找离自己最近的Provider
,获取其value
;
value
变化,插入Provider
的组件都会重新渲染;
没有匹配到Provider
,使用默认值;其他情况均不使用默认参数;
新旧 value
值对比,用的是和 Object.is
相同的算法
案例1:更改主题
/* Header.css */
.header{
position: fixed;
top: 0;
left: 0;
right: 0;
height: 50px;
line-height: 50px;
background-color: black;
color: #fff;
}
.header.red{
background-color: red;
}
.header.pink{
background-color: pink;
}
// context.js
const ThemeContext = React.createContext('black')
export {
ThemeContext
}
// Header.jsx
import "./Header.css";
import { ThemeContext } from "./context";
export default class Header extends React.Component {
render() {
return (
<ThemeContext.Consumer>
{(theme) => (
<header className={`header ${theme}`}>{this.props.children}</header>
)}
</ThemeContext.Consumer>
);
}
}
// index.jsx
import Header from "./context_comps/Header";
import { ThemeContext } from "./context_comps/context";
class App extends React.Component {
state = {
theme: "black",
};
render() {
return (
<div>
<ThemeContext.Provider value={this.state.theme}>
<Header>This is a title</Header>
</ThemeContext.Provider>
</div>
);
}
}
案例2:使用组合方式解决数据传递问题
class App extends React.Component {
state = {
headerTitle: "This is a title",
cityInfo: {
name: "shanghai",
text: "上海",
},
cityData: [
{
name: "beijing",
text: "北京",
},
{
name: "shanghai",
text: "上海",
},
{
name: "guangzhou",
text: "广州",
},
],
};
changeCity(cityInfo) {
this.setState({
cityInfo,
});
}
render() {
return (
<>
<Header
text={this.state.headerTitle}
citySelector={
<Selector
cityData={this.state.cityData}
cityInfo={this.state.cityInfo}
changeCity={this.changeCity.bind(this)}
/>
}
></Header>
<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].text
})}
>
{this.props.cityData.map((item, index) => {
return <option value={item.name} key={index}>{item.text}</option>;
})}
</select>
);
}
}
contextType
-
class
的静态属性 -
contextType
->React.createContext()
->Context
对象 -
this
->context
->CityContext
- 在生命周期函数和
render
函数中都可以访问 - 语义不太好
static contextType = CityContext;
componentDidMount() {
console.log(this.context);
}
render() {
console.log(this.context);
}
案例3
// index.jsx
import Home from "./views/Home";
import { btnStyle } from "./config";
import { BtnStyeContext, LoginStatusContext } from "./context_comps/context";
class App extends React.Component {
state = {
style: btnStyle.success,
loginStatus: false,
};
doClick(e) {
console.log(e.target.textContent);
}
login() {
this.setState({
loginStatus: !this.state.loginStatus,
});
}
render() {
return (
<div>
<BtnStyeContext.Provider
value={{
style: this.state.style,
doClick: this.doClick,
}}
>
<LoginStatusContext.Provider
value={{
status: this.state.loginStatus,
login: this.login.bind(this),
}}
>
<Home />
</LoginStatusContext.Provider>
</BtnStyeContext.Provider>
</div>
);
}
}
ReactDOM.render(<App />, document.querySelector("#app"));
// Home.jsx
import { BtnStyeContext, LoginStatusContext } from "../context_comps/context";
import Header from "../components/Header";
import Main from "../components/Main";
import Footer from "../components/Footer";
export default class Home extends React.Component {
render() {
return (
<div className="page_home">
<Header></Header>
<hr />
<Main></Main>
<hr />
<Footer></Footer>
<hr />
</div>
);
}
}
// Header.jsx
import { LoginStatusContext } from "../context_comps/context";
import StButton from "./StButton";
export default 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>
);
}
}
// Main.jsx
import { LoginStatusContext } from "../context_comps/context";
import StButton from "./StButton";
export default class Main extends React.Component{
render() {
return (
<LoginStatusContext.Consumer>
{({ status, login }) => (
<div className="main">
<h1>Main</h1>
<StButton>Main({status ? " 已登录" : " 未登录"})</StButton>
<button onClick={login}>登录/注销</button>
</div>
)}
</LoginStatusContext.Consumer>
);
}
}
// Footer.jsx
import { LoginStatusContext } from "../context_comps/context";
import StButton from "./StButton";
export default class Footer extends React.Component {
render() {
return (
<LoginStatusContext.Consumer>
{({ status, login }) => (
<div className="footer">
<h1>Footer</h1>
<StButton>Footer({status ? " 已登录" : " 未登录"})</StButton>
<button onClick={login}>登录/注销</button>
</div>
)}
</LoginStatusContext.Consumer>
);
}
}
// StButton.jsx
import { BtnStyeContext } from "../context_comps/context";
export default class StButton extends React.Component {
render() {
return (
<BtnStyeContext.Consumer>
// {({ style, doClick }) => (
// <button style={style} onClick={doClick} {...this.props}></button>
// )}
</BtnStyeContext.Consumer>
);
}
}
// context.js
// 提供 context
import { btnStyle } from "../config";
const BtnStyeContext = React.createContext({
style: btnStyle.primary,
doClick: () => {},
});
const LoginStatusContext = React.createContext({
status: false,
login: () => {},
});
export { BtnStyeContext, LoginStatusContext };
// btnStyle
export const btnStyle = {
primary: {
color: "#fff",
backgroundColor: "blue",
},
success: {
color: "#fff",
backgroundColor: "green",
},
warning: {
color: "#000",
backgroundColor: "orange",
},
danger: {
color: "#fff",
backgroundColor: "red",
},
}
Fragment 和短语法应用
React.Fragment
组件,和 Document.createDocumentFragment()
类似,不会在原有 Dom
结构中增加冗余标签
现阶段,Fragment
组件除了 key
属性,不支持其他任何属性
短语法不支持 Key
,和其他任何语法
<>xxx</>
class Table extends React.Component {
render() {
return (
<table border={1}>
<caption>Private Infomation</caption>
<thead>
<tr>
<TableHeader />
</tr>
</thead>
<tbody>
<TableBody />
</tbody>
</table>
);
}
}
class TableHeader extends React.Component {
state = {
headers: ["Name", "ID", "Age"],
};
render() {
return (
<React.Fragment>
{this.state.headers.map((item, index) => (
<th key={index}>{item}</th>
))}
</React.Fragment>
);
}
}
class TableBody extends React.Component {
state = {
info: [
{
name: "zsss",
id: "897",
age: "177",
},
],
};
render() {
return (
<React.Fragment>
{this.state.info.map((item) => (
<tr>
<td>{item.name}</td>
<td>{item.id}</td>
<td>{item.age}</td>
</tr>
))}
</React.Fragment>
);
}
}
class App extends React.Component {
render() {
return <Table></Table>;
}
}