企业🤖AI Agent构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
## 1.Context(上下文) * 在某些场景下,你想在整个组件树中传递数据,但却不想手动地在每一层传递属性。你可以直接在 React 中使用强大的`context`API解决上述问题 * 在一个典型的 React 应用中,数据是通过 props 属性自上而下(由父及子)进行传递的,但这种做法对于某些类型的属性而言是极其繁琐的(例如:地区偏好,UI 主题),这些属性是应用程序中许多组件都需要的。Context 提供了一种在组件之间共享此类值的方式,而不必显式地通过组件树的逐层传递 props ![contextapi](http://img.zhufengpeixun.cn/contextapi.gif) ### 1.1 类组件使用 ~~~ import React from 'react'; import ReactDOM from 'react-dom'; let ThemeContext = React.createContext(); class Title extends React.Component { static contextType = ThemeContext render() { return ( <div style={{ border: `5px solid ${this.context.color}` }}> Title </div> ) } } class Header extends React.Component { static contextType = ThemeContext render() { return ( <div style={{ border: `5px solid ${this.context.color}` }}> Header <Title /> </div> ) } } class Content extends React.Component { static contextType = ThemeContext render() { return ( <div style={{ border: `5px solid ${this.context.color}` }}> Content <button onClick={() => this.context.changeColor('red')}>变红</button> <button onClick={() => this.context.changeColor('green')}>变绿</button> </div> ) } } class Main extends React.Component { static contextType = ThemeContext render() { return ( <div style={{ border: `5px solid ${this.context.color}` }}> Main <Content /> </div> ) } } class Panel extends React.Component { state = { color: 'green' } changeColor = (color) => { this.setState({ color }); } render() { let value = { color: this.state.color, changeColor: this.changeColor }; //Provider提供者,它负责向下层所有的组件提供数据value return ( <ThemeContext.Provider value={value}> <div style={{ border: `5px solid ${this.state.color}`, width: 300 }}> Panel <Header /> <Main /> </div> </ThemeContext.Provider> ) } } ReactDOM.render(<Panel />, document.getElementById('root')); ~~~ ### 1.2 函数组件使用 ~~~ import React, { Component } from 'react'; import ReactDOM from 'react-dom'; let ThemeContext = React.createContext('theme'); class Header extends Component { render() { return ( <ThemeContext.Consumer> { value => ( <div style={{ border: `5px solid ${value.color}`, padding: 5 }}> header <Title /> </div> ) } </ThemeContext.Consumer> ) } } class Title extends Component { static contextType = ThemeContext; render() { return ( <ThemeContext.Consumer> { value => ( <div style={{border: `5px solid ${value.color}` }}> title </div> ) } </ThemeContext.Consumer> ) } } class Main extends Component { static contextType = ThemeContext; render() { return ( <ThemeContext.Consumer> { value => ( <div style={{ border: `5px solid ${value.color}`, margin: 5, padding: 5 }}> main <Content /> </div> ) } </ThemeContext.Consumer> ) } } class Content extends Component { static contextType = ThemeContext; render() { return ( <ThemeContext.Consumer> { value => ( <div style={{border: `5px solid ${value.color}`, padding: 5 }}> Content <button onClick={() =>value.changeColor('red')} style={{color:'red'}}>红色</button> <button onClick={() => value.changeColor('green')} style={{color:'green'}}>绿色</button> </div> ) } </ThemeContext.Consumer> ) } } class Page extends Component { constructor() { super(); this.state = { color: 'red' }; } changeColor = (color) => { this.setState({ color }) } render() { let contextVal = {changeColor: this.changeColor,color:this.state.color }; return ( <ThemeContext.Provider value={contextVal}> <div style={{margin:'10px', border: `5px solid ${this.state.color}`, padding: 5, width: 200 }}> page <Header /> <Main /> </div> </ThemeContext.Provider> ) } } ReactDOM.render(<Page />, document.querySelector('#root')); ~~~ ### 1.3 函数组件实现 ~~~ function createContext() { let value; class Provider extends React.Component { constructor(props) { super(props); value = props.value this.state = {}; } static getDerivedStateFromProps(nextProps, prevState) { value = nextProps.value; return {}; } render() { return this.props.children; } } class Consumer extends React.Component { constructor(props) { super(props); } render() { return this.props.children(value); } } return { Provider, Consumer } } let ThemeContext = createContext('theme'); ~~~ ## 3\. 高阶组件 * 高阶组件就是一个函数,传给它一个组件,它返回一个新的组件 * 高阶组件的作用其实就是为了组件之间的代码复用 ~~~ const NewComponent = higherOrderComponent(OldComponent) ~~~ ### 3.1 日志组件 #### 3.1 手工实现 ~~~ import React, { Component } from 'react'; import ReactDOM from 'react-dom'; class App extends Component { componentWillMount() { this.start = Date.now(); } componentDidMount() { console.log((Date.now() - this.start) + 'ms') } render() { return <div>App</div> } } ReactDOM.render(<App />, document.getElementById('root')); ~~~ #### 3.2 高阶组件 ~~~ import React,{Component} from 'react'; import ReactDOM from 'react-dom'; const logger = (WrappedComponent) => { class LoggerComponent extends Component { componentWillMount(){ this.start = Date.now(); } componentDidMount(){ console.log((Date.now() - this.start)+'ms') } render () { return <WrappedComponent /> } } return LoggerComponent; } let Hello = logger(props=><h1>hello</h1>); ReactDOM.render(<Hello />, document.getElementById('root')); ~~~ ### 3.2 多层高阶组件 #### 3.2.1 从localStorage中加载 ~~~ import React,{Component} from 'react'; import ReactDOM from 'react-dom'; const fromLocal = (WrappedComponent,name) =>{ class NewComponent extends Component{ constructor(){ super(); this.state = {value:null}; } componentWillMount(){ let value = localStorage.getItem(name); this.setState({value}); } render(){ return <WrappedComponent value={this.state.value}/> } } return NewComponent; } const UserName = ({value})=>( <input defaultValue = {value}/> ) const UserNameFromLocal = fromLocal(UserName,'username'); ReactDOM.render(<UserNameFromLocal />, document.getElementById('root')); ~~~ #### 3.2.2 从ajax中加载 ~~~ import React,{Component} from 'react'; import ReactDOM from 'react-dom'; const fromLocal = (WrappedComponent,name) =>{ class NewComponent extends Component{ constructor(){ super(); this.state = {id:null}; } componentWillMount(){ let id = localStorage.getItem(name); this.setState({id}); } render(){ return <WrappedComponent id={this.state.id}/> } } return NewComponent; } const fromAjax = (WrappedComponent) =>{ class NewComponent extends Component{ constructor(){ super(); this.state = {value:{}}; } componentDidMount(){ fetch(`/${this.props.id}.json`).then(response=>response.json()).then(value=>{ this.setState({value}); }); } render(){ return <WrappedComponent value={this.state.value}/> } } return NewComponent; } const UserName = ({value})=>{ return <input defaultValue = {value.username}/>; } const UserNameFromAjax = fromAjax(UserName); const UserNameFromLocal = fromLocal(UserNameFromAjax,'id'); ReactDOM.render(<UserNameFromLocal />, document.getElementById('root')); ~~~ translate.json ~~~ { "zhangsan": "张三" } ~~~ ## 4\. render props * [render-props](https://zh-hans.reactjs.org/docs/render-props.html) * `render prop`是指一种在 React 组件之间使用一个值为函数的 prop 共享代码的简单技术 * 具有 render prop 的组件接受一个函数,该函数返回一个 React 元素并调用它而不是实现自己的渲染逻辑 * render prop 是一个用于告知组件需要渲染什么内容的函数 prop * 这也是逻辑复用的一种方式 ~~~ <DataProvider render={data => ( <h1>Hello {data.target}</h1> )}/> ~~~ ### 4.1 原生实现 ~~~ class MouseTracker extends React.Component { constructor(props) { super(props); this.state = { x: 0, y: 0 }; } handleMouseMove = (event) => { this.setState({ x: event.clientX, y: event.clientY }); } render() { return ( <div onMouseMove={this.handleMouseMove}> <h1>移动鼠标!</h1> <p>当前的鼠标位置是 ({this.state.x}, {this.state.y})</p> </div> ); } } ~~~ ### 4.2 children ~~~ class MouseTracker extends React.Component { constructor(props) { super(props); this.state = { x: 0, y: 0 }; } handleMouseMove = (event) => { this.setState({ x: event.clientX, y: event.clientY }); } render() { return ( <div onMouseMove={this.handleMouseMove}> {this.props.children(this.state)} </div> ); } } ReactDOM.render(< MouseTracker > { props=>( <> <h1>移动鼠标!</h1> <p>当前的鼠标位置是 ({props.x}, {props.y})</p> </> ) } </ MouseTracker>, document.getElementById('root')); ~~~ ### 4.3 render属性 ~~~ import React,{Component} from 'react'; import ReactDOM from 'react-dom'; class MouseTracker extends React.Component { constructor(props) { super(props); this.state = { x: 0, y: 0 }; } handleMouseMove = (event) => { this.setState({ x: event.clientX, y: event.clientY }); } render() { return ( <div onMouseMove={this.handleMouseMove}> {this.props.render(this.state)} </div> ); } } ReactDOM.render(< MouseTracker render={params=>( <> <h1>移动鼠标!</h1> <p>当前的鼠标位置是 ({params.x}, {params.y})</p> </> )} />, document.getElementById('root')); ~~~ ### 4.4 HOC ~~~ class MouseTracker extends React.Component { constructor(props) { super(props); this.state = { x: 0, y: 0 }; } handleMouseMove = (event) => { this.setState({ x: event.clientX, y: event.clientY }); } render() { return ( <div onMouseMove={this.handleMouseMove}> {this.props.render(this.state)} </div> ); } } function withMouse(Component){ return ( (props)=><MouseTracker render={mouse=><Component {...props} {...mouse}/>}/> ) } let App = withMouse(props=>( <> <h1>移动鼠标!</h1> <p>当前的鼠标位置是 ({props.x}, {props.y})</p> </> )); ReactDOM.render(<App/>, document.getElementById('root')); ~~~ ## 5\. 插槽(Portals) * Portals 提供了一种很好的方法,将子节点渲染到父组件 DOM 层次结构之外的 DOM 节点。 ~~~ ReactDOM.createPortal(child, container) ~~~ * 第一个参数(child)是任何可渲染的 React 子元素,例如一个元素,字符串或 片段(fragment) * 第二个参数(container)则是一个 DOM 元素 index.html ~~~ <div id="modal-root"></div> ~~~ index.js ~~~ import React,{Component} from 'react'; import ReactDOM from 'react-dom'; import './modal.css'; class Modal extends Component{ constructor() { super(); this.modal=document.querySelector('#modal-root'); } render() { return ReactDOM.createPortal(this.props.children,this.modal); } } class Page extends Component{ constructor() { super(); this.state={show:false}; } handleClick=() => { this.setState({show:!this.state.show}); } render() { return ( <div> <button onClick={this.handleClick}>显示模态窗口</button> { this.state.show&&<Modal> <div id="modal" className="modal"> <div className="modal-content" id="modal-content"> 内容 <button onClick={this.handleClick}>关闭</button> </div> </div> </Modal> } </div> ) } } ReactDOM.render(<Page/>,document.querySelector('#root')); ~~~ modal.css ~~~ .modal{ position: fixed; left:0; top:0; right:0; bottom:0; background: rgba(0,0,0,.5); display: block; } @keyframes zoom{ from{transform:scale(0);} to{transform:scale(1);} } .modal .modal-content{ width:50%; height:50%; background: white; border-radius: 10px; margin:100px auto; display:flex; flex-direction: row; justify-content: center; align-items: center; animation: zoom .6s; } ~~~ ~~~ import React from 'react'; /** 写一个函数,然后参数是一个组件 高阶组件用来解决组件间的逻辑复用问题 高阶 高阶函数 函数可以作为函数的参数或者返回值,这就叫同阶函数 高阶 高阶组件 组件可以作为函数的参数或者返回值,这就叫同阶组件 */ function withLogger(WrappedComponent){ return class extends React.Component{ componentWillMount(){ this.start = Date.now(); } componentDidMount(){ console.log(`此组件渲染一共花了${Date.now()-this.start}ms`); } render(){ return <WrappedComponent/> } }; } let Hello = ()=><div>hello</div>; Hello = withLogger(Hello); export default Hello; ~~~ ~~~ import React from 'react'; class Form extends React.Component{ render(){ return ( <form> <UserName/> <Password/> </form> ) } } function fromLocal(WrappedComponent,key){ return class extends React.Component{ constructor(props){ super(props); this.state = {value:''}; } componentWillMount(){ this.setState({ value:localStorage.getItem(key) }); } render(){//this.state.value = zhangsan return <WrappedComponent value={this.state.value} /> } } } function fromAjax(WrappedComponent){ return class extends React.Component{ constructor(props){ super(props); this.state = {value:''}; } componentWillMount(){ //一方面要从属性中取值到父组件传过来的属性值value let value = this.props.value;//username /username.json fetch(`/${value}.json`).then(res=>res.json()).then(result=>{ console.log(result); this.setState({value:result.value});//result.value=张三 }); } render(){//WrappedComponent=UserName return <WrappedComponent value={this.state.value} /> } } } class UserName extends React.Component{ render(){ return <>用户名:<input value={this.props.value} /></> } } UserName = fromAjax(UserName); UserName = fromLocal(UserName,'username'); class Password extends React.Component{ render(){ return <>密码:<input value={this.props.value} onChange={this.props.handleChange}/></> } } Password = fromAjax(Password); Password = fromLocal(Password,'password'); export default Form; ~~~ ![](https://img.kancloud.cn/a1/34/a13452772c6645e7b9593c02b8098047_861x585.png) ~~~ import React,{Component} from 'react'; class MouseTracker extends React.Component{ state = {x:0,y:0} handleMouseMove = (event)=>{ this.setState({ x:event.clientX, y:event.clientY }); } render(){ return ( <div onMouseMove={this.handleMouseMove} > {this.props.render(this.state)} </div> ) } } export default MouseTracker; ~~~ ![](https://img.kancloud.cn/4c/17/4c17b8860e3cf23428842578b6f613a1_1177x649.png) ![](https://img.kancloud.cn/8b/0a/8b0ac370af29a7f9beae31ffbb5e571f_908x686.png) ![](https://img.kancloud.cn/2e/0c/2e0cda090972d6f76cf628aec143dec2_881x405.png)