这一篇我主要是想记录一下关于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(切片编程的思想)实现对公共逻辑的复用。最后返回组件。
- 代码会冗余
- 工作量大
- 逻辑耦合
- 高阶组件是为了解决逻辑复用的问题。但是如果包裹太多的话,就又会是的逻辑非常的复杂和难以维护。所以还是要适当的使用高阶组件
<!--日志的高阶组件-->
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是把一个渲染的函数传递给了一个组件 一般并不会产生新组件
*
*
*/