'假的'必学必会之React学习笔记之六

388 阅读4分钟

这一篇我主要是想记录一下关于context高阶组件render props

  • context 上下文

    • 当父子,祖孙级组件甚至更深层嵌套的组件需要相互之间的传递信息,进行通信。我们必不可少的需要使用的context的方式来传递和接受组建的信息。
import React from 'react';
import ReactDOM from 'react-dom';
let ThemeContext = React.createContext();
let HeaderContext = React.createContext();
//一个组件什么时候会更新,是当属性或者状态发生变化的时候
function createContext(initialValue) {
    class Provider extends React.Component {
        static xx = initialValue;
        constructor(props) {
            super(props);
            //Provider.xx = props.value;
        }
        /*  componentWillReceiveProps(nextProps) {
             contextValue = nextProps.value;
         } */
        /* static getDerivedStateFromProps(nextProps, prevState) {
            Provider.xx = nextProps.value;
            return null;//返回值是新的状态对象  this.state
        } */
        render() {
            Provider.xx = this.props.value;
            console.log('Provider.render');
            return this.props.children;
        }
    }
    class Consumer extends React.Component {
        render() {
            return this.props.children(Provider.xx);
        }
    }
    return { Provider, Consumer };
}
//let ThemeContext = createContext(null);
/* class Title extends React.Component {
    static contextType = ThemeContext;
    constructor(props, context) {
        super(props);
        //context就代表上下文对象=ThemeContext.Provider.contextValue = this.context
        //在构造函数里,是不能通过this.context拿 到上下文对象的
        console.log('Title.constructor.context', context);
    }
    render() {
        //context是一个内部被保护的变量,所以不能直接赋值
        //this.context2 = Title.contextType.Provider.contextValue;
        console.log('Title.render.context', this.context);
        return (
            <div style={{ border: `5px solid ${this.context.color}` }}>
                Title
            </div>
        )
    }
} */

function Title() {
    return (
        <HeaderContext.Consumer>
            {
                value => {
                    console.log('Title', value);
                    return (<div>title</div>)
                }
            }
        </HeaderContext.Consumer>
    )
}
function Header(props) {
    // Consumer消费者,意思是我要消费上下文中的数据Provider中的value
    return (
        <HeaderContext.Provider value={{ color: 'yellow', age: 10 }}>
            <div style={{ border: `5px solid yellow` }}>
                Header
                       <Title />
            </div>
        </HeaderContext.Provider>

    )
}

function Content(props) {
    // Consumer消费者,意思是我要消费上下文中的数据Provider中的value
    return (
        <ThemeContext.Consumer>
            {
                (value) => (
                    <div style={{ border: `5px solid ${value.color}` }}>
                        Content
                        s<button onClick={() => value.changeColor('red')}>变红</button>
                        <button onClick={() => value.changeColor('green')}>变绿</button>
                    </div>
                )
            }
        </ThemeContext.Consumer>

    )
}

function Main(props) {
    //Consumer消费者,意思是我要消费上下文中的数据Provider中的value
    return (
        <ThemeContext.Consumer>
            {
                (value) => (
                    <div style={{ border: `5px solid ${value.color}` }}>
                        Main
                       <Content />
                    </div>
                )
            }
        </ThemeContext.Consumer>

    )
}

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'));
  • 高阶组件
  • 高阶组件我个人理解和高阶函数很像,概念也类似。是吧组件当做一个参数传入导函数中,然后通过AOP(切片编程的思想)实现对公共逻辑的复用。最后返回组件。
  1. 代码会冗余
  2. 工作量大
  3. 逻辑耦合
  4. 高阶组件是为了解决逻辑复用的问题。但是如果包裹太多的话,就又会是的逻辑非常的复杂和难以维护。所以还是要适当的使用高阶组件
<!--日志的高阶组件-->
function withLogger(OldComponent) {
    return class extends React.Component {
        componentWillMount() {
            this.start = Date.now();
        }
        componentDidMount() {
            console.log((Date.now() - this.start) + 'ms');
        }
        render() {
            return (
                <div>
                    <div>hello</div>
                    <OldComponent {...this.props} />
                    <div>world</div>
                </div>
            )
        }
    }
}
let LoggerApp = withLogger(App);
ReactDOM.render(<LoggerApp name='zhufeng' />, document.getElementById('root'));
<!--两层嵌套的高阶组件-->
<!--多层嵌套的高阶组件,有个原则:嵌套包装的时候由内向外包装,解套的时候由外向内剥离。所以其实我们最终在页面上使用的是内层嵌套的高阶组件,因为他会去调用外层的嵌套逻辑。从而达到我们要的效果,如下:-->
import React from 'react';
import ReactDOM from 'react-dom';
//从localStorage中加载数据 localStorage.getItem('username');  zhangsan
function loadFromLocal(AjaxUserName, name) {
    return class LocalUserName extends React.Component {
        state = { value: null }
        componentDidMount() {
            let value = localStorage.getItem(name);
            this.setState({ value });
        }
        render() {
            return <AjaxUserName value={this.state.value} />;
        }
    }
}
//接收一个属性里面放着英文名,然后通过调用远程服务器端接口,得到此英文名对应的中文名,再赋值给this.state.value
function loadFromAjax(LocalUserName) {
    return class AjaxUserName extends React.Component {
        state = { value: null }
        componentDidMount() {
            //let value = this.props.value;//从属性对象中获取value的值
            fetch('/translation.json').then(response => response.json()).then(data => {
                //data={"tom": "汤姆","jerry": "杰瑞"}
                this.setState({ value: data[this.props.value] });
            });
        }
        render() {
            return <LocalUserName value={this.state.value} />
        }
    }
}

const UserName = (props) => {
    return <input defaultValue={props.value} />
}
const AjaxUserName = loadFromAjax(UserName);
let LocalUserName = loadFromLocal(AjaxUserName, 'username');
ReactDOM.render(<div>
    <LocalUserName />
</div>, document.getElementById('root'));

/**
 *
 * hooks 实现逻辑复用的方式
 * 但是会比高阶组件优雅的多
 */
  • render props
import React from 'react';
import ReactDOM from 'react-dom';
class MouseTracker extends React.Component {
   state = {
       x: 0, y: 0
   }
   componentDidMount() {
       console.log('componentDidMount');
   }
   handleMouseMove = (event) => {
       this.setState({
           x: event.clientX,
           y: event.clientY
       })
   }
   render() {
       return (
           <div onMouseMove={this.handleMouseMove}>
               <span>hello</span>
           //①{this.props.children(this.state)
           //②{this.prpos.render(this.state)} 
          <!--③--> {this.props.render(this.state)}
               <span>world</span>
           </div>
       )
   }
}
// ①传函数组为一个子元素的写法
ReactDOM.render(<MouseTracker>{
 props => {
   return (
   <>
       <h1>请移动鼠标</h1>
       <p>当前鼠标的位置 x={props.x} y={props.y}</p>
   </>
   )
}  
}</MouseTracker>, root)
// ②render属性的写法
ReactDOM.render(<MouseTracker         render = { props => {
       return (
       <>
           <h1>请移动鼠标</h1>
           <p>当前鼠标的位置 x={props.x} y={props.y}</p>
       </>
       )
   }/>
},root)
//③高阶组件的写法
function withMouseTracker(OldComponent) {
   return props => (
       <MouseTracker render={props => <OldComponent {...props} />} />
   )
}
let App = props => (
   <React.Fragment>
       <h1>请移动鼠标</h1>
       <p>当前鼠标的位置 x={props.x} y={props.y}</p>
   </React.Fragment>
)
let WithMouseTrackerApp = withMouseTracker(App);
ReactDOM.render(<div>
   <WithMouseTrackerApp />
</div>, document.getElementById('root'));

/**
* 把children实现成一个函数。在组件里可以调用此函数,从而渲染函数返回虚拟DOM
* 高阶组件和render是可以互相改写的,也可以互相转化
*/
/**
* 高阶组件HOC和render方法他们的区别并不于谁来决定渲染出来的内容 ,渲染出来都有
*  根本区别在于高阶组件传递的是一个函数组件 render方法传递是一个函数
*
*/
/**
* 高阶组件是把一个组件传递给了 一 个函数  一般会产生一个新组件
* render是把一个渲染的函数传递给了一个组件 一般并不会产生新组件
*
*
*/