美文网首页
React 中的 context(7)

React 中的 context(7)

作者: 示十 | 来源:发表于2022-09-26 23:40 被阅读0次

    Context

    作用:给整个组件树共享全局的数据

    最适合的场景:杂乱无章的组件都需要同一些数据

    如果单纯为了不层层传递属性的话, Context 是不合适的,因为 Context 会弱化及污染组件的纯度,导致组件复用性降低

    Context API

    • Context.Provider 数据提供者
      通过 React.createContext 创建的一个上下文对象里的组件,Provider 组件中可以插入其他组件,来订阅这个 Context
      通过 Providervalue 将数据传递给 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>;
      }
    }
    

    相关文章

      网友评论

          本文标题:React 中的 context(7)

          本文链接:https://www.haomeiwen.com/subject/ifunortx.html