如果想对第三方组件进行功能扩展,可能就会用到高阶组件
import React from 'react';
import ReactDOM from 'react-dom';
class Button extends React.Component{
state = {name:'张三'}
render(){
return <button name={this.state.name} title={this.props.title}/>
}
}
const wrapper = OldComponent =>{
return class NewComponent extends OldComponent{
state = {number:0}
handleClick = ()=>{
this.setState({number:this.state.number+1});
}
render(){
let renderElement = super.render();
let newProps = {
...renderElement.props,
...this.state,
onClick:this.handleClick
}
return React.cloneElement(
renderElement,
newProps,
this.state.number
);
}
}
}
let WrappedButton = wrapper(Button);
ReactDOM.render(
<WrappedButton title="标题"/>, document.getElementById('root'));
注:上面的代码中,由于子类的state被赋值为{number:0},该对象会覆盖父类中的state,所以子类中就取不到父类中定义的state = {name:'张三'}中的name属性了
需要注意,根据MDN的文档描述,以下两种写法是等价的:
developer.mozilla.org/en-US/docs/…
class Button extends React.Component{
state;
constructor () {
this.state = {name:'张三'}
}
render(){
return <button name={this.state.name} title={this.props.title}/>
}
}
和
class Button extends React.Component{
constructor () {
this.state = {name:'张三'}
}
render(){
return <button name={this.state.name} title={this.props.title}/>
}
}
整个过程,并没有创建父组件的实例,而只是通过super.render()得到了父组件对应的虚拟DOM,返回了一个新的节点
为便于理解,我们将代码稍作修改,主要是把state属性放到constructor构造函数中去
import React from 'react';
import ReactDOM from 'react-dom';
class Button extends React.Component{
constructor (props) {
super(props);
this.state = {name:'张三'}
}
render(){
return <button name={this.state.name} title={this.props.title}/>
}
}
const wrapper = OldComponent =>{
return class NewComponent extends OldComponent{
constructor (props) {
super(props);
this.state = {number:0}
}
handleClick = ()=>{
this.setState({number:this.state.number+1});
}
render(){
let renderElement = super.render();
let newProps = {
...renderElement.props,
...this.state,
onClick:this.handleClick
}
return React.cloneElement(
renderElement,
newProps,
this.state.number
);
}
}
}
let WrappedButton = wrapper(Button);
ReactDOM.render(
<WrappedButton title="标题"/>, document.getElementById('root'));
上面的代码在执行完第15行以后,this.state就变成了{name:'张三'}(父组件中赋的值),紧接着在第16行我们又将this.state重新赋值为{number:0},所以原来的{name:'张三'}被覆盖,之后就再也取不到了
所以解决的方案是将第16行改为
this.state = {...this.state, number:0}
这样我们就也可以用父组件中赋值过的name了
比较优雅的写法还可以用注解:
@wrapper
class Button extends React.Component{
constructor (props) {
super(props);
this.state = {name:'张三'}
}
render(){
return <button name={this.state.name} title={this.props.title}/>
}
}
\
renderProps:组件的属性或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) => (
<div>
<h1>移动鼠标!</h1>
<p>当前的鼠标位置是 ({props.x}, {props.y})</p>
</div>
)
}
</MouseTracker >, document.getElementById('root'));
HOC高阶组件和renderProps可以互相转换