一文详解 React 组件类型
Posted CSDN
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一文详解 React 组件类型相关的知识,希望对你有一定的参考价值。
本文的目标是让开发者清晰地了解 React 组件类型,哪些在现代 React 应用中依然在使用,以及为何一些类型现在不再使用了。
作者 | Robin Wieruch
译者 | 弯月
责编 | 屠敏
出品 | CSDN(ID:CSDNNews)
以下为译文:
尽管React从2013年发布到现在并没有引入太多重大改变,但不同类型的React组件也出现了不少。一些组件类型和组件设计模式今天依然在使用,它们已成了构建React应用程序的标准,而另一些类型的组件只会在旧的应用和新手教学中出现。
在这篇文章中,我想通过层次化的方式向初学React的人介绍一下不同的React组件和React设计模式。读完本文后,你应当能够从旧的应用和入门文章中分辨出不同类型的React组件,并能够自信地编写自己的现代React组件。
React createClass组件
我们要从React的createClass组件说起。createClass方法为开发者提供了一个工厂方法,无需编写javascript类就可以创建React类组件。在JavaScript ES6出现之前,这种方法是创建React组件的标准方法,因为在JavaScript ES5时代还无法使用类语法:
var App = React.createClass({
getInitialState: function() {
return {
value: '',
};
},
onChange: function(event) {
this.setState({ value: event.target.value });
},
render: function() {
return (
<div>
<h1>Hello React "createClass" Component!</h1>
<input
value={this.state.value}
type="text"
onChange={this.onChange}
/>
<p>{this.state.value}</p>
</div>
);
},
});
createClass()工厂方法接受一个对象,该对象定义了React组件中的方法。其中,getInitialState()方法用来设置React组件的初始状态,还有必须的render()方法用来显示JSX形式的组件。给对象传递更多函数即可添加更多的“方法”(如onChange())。
还可以使用生命周期方法来管理副作用。例如,为了随时将输入框中的值保存到浏览器的local storage中,可以利用componentDidUpdate()这个生命周期方法,只需要将该函数传递给工厂函数的对象即可。而且,local storage中的值也可以在组件接收初始状态的时候读出来:
var App = React.createClass({
getInitialState: function() {
return {
value: localStorage.getItem('myValueInLocalStorage') || '',
};
},
componentDidUpdate: function() {
localStorage.setItem('myValueInLocalStorage', this.state.value);
},
onChange: function(event) {
this.setState({ value: event.target.value });
},
render: function() {
return (
<div>
<h1>Hello React "createClass" Component!</h1>
<input
value={this.state.value}
type="text"
onChange={this.onChange}
/>
<p>{this.state.value}</p>
</div>
);
},
});
每次重新加载或刷新浏览器时,之前在输入框中输入过的、保存在local storage中的初始状态就会在组件初次mount的时候显示出来。
注意:React的createClass方法现在已经不在React的核心包中了。如果你想尝试下,就必须要安装另一个包:npm install create-react-class。
React Mixin
React Mixin是在React提出可重用组件逻辑的高级设计方式时加入的。利用Mixin可以将React组件中的逻辑提取出来作为独立的对象使用。在使用Mixin对象时,Mixin中的所有功能都会被引入到组件中:
var localStorageMixin = {
getInitialState: function() {
return {
value: localStorage.getItem('myValueInLocalStorage') || '',
};
},
setLocalStorage: function(value) {
localStorage.setItem('myValueInLocalStorage', value);
},
};
var App = React.createClass({
mixins: [localStorageMixin],
componentDidUpdate: function() {
this.setLocalStorage(this.state.value);
},
onChange: function(event) {
this.setState({ value: event.target.value });
},
render: function() {
return (
<div>
<h1>Hello React "createClass" Component with Mixin!</h1>
<input
value={this.state.value}
type="text"
onChange={this.onChange}
/>
<p>{this.state.value}</p>
</div>
);
},
});
在这个例子中,Mixin提供了组件的初始状态,而该初始状态是从local storage中读取的,并且还利用setLocalStorage()扩展原来的组件,该函数之后会在组件中被调用。为了让Mixin更灵活,我们可以使用一个返回函数的对象:
function getLocalStorageMixin(localStorageKey) {
return {
getInitialState: function() {
return { value: localStorage.getItem(localStorageKey) || '' };
},
setLocalStorage: function(value) {
localStorage.setItem(localStorageKey, value);
},
};
}
var App = React.createClass({
mixins: [getLocalStorageMixin('myValueInLocalStorage')],
...
});
不过,现代React应用程序已经不再使用Mixin了,因为它们会带来一些负面作用。关于Mixin的细节和消亡过程可以阅读这里(https://reactjs.org/blog/2016/07/13/mixins-considered-harmful.html)
React类组件
React类组件是在JavaScript ES6时引入的,因为直到ES6才支持JS类。有时候它们也被称为React ES6类组件。至少有了JavaScript ES6之后,就不需要使用React的createClass方法了。JS自己终于支持类了:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
value: '',
};
this.onChange = this.onChange.bind(this);
}
onChange(event) {
this.setState({ value: event.target.value });
}
render() {
return (
<div>
<h1>Hello React ES6 Class Component!</h1>
<input
value={this.state.value}
type="text"
onChange={this.onChange}
/>
<p>{this.state.value}</p>
</div>
);
}
}
使用JavaScript类编写的React Component有个类似于类构造器的方法,主要用于让React设置初始状态,或者绑定方法。还有必须的render方法用于返回JSX的输出。React组件的所有内部逻辑都通过类组件定义中的面向对象继承,从extends React.Component获得。但是,除了这种用法之外,我并不推荐进一步使用类继承,相反,应当主要使用类组合(composition)。
注意:利用JavaScript类定义React组件时还可以使用了另一种语法,通过JavaScript ES6的箭头函数来自动绑定React组件中的方法:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
value: '',
};
}
onChange = event => {
this.setState({ value: event.target.value });
};
render() {
return (
<div>
<h1>Hello React ES6 Class Component!</h1>
<input
value={this.state.value}
type="text"
onChange={this.onChange}
/>
<p>{this.state.value}</p>
</div>
);
}
}
React类组件提供几个生命周期方法,用于mount、update和unmount等。比如前面的local storage的例子,可以在生命周期方法中以副作用的方式来执行这些操作——即,将输入框中的最新值保存到local storage中,而在构造函数中可以根据local storage的值来设置初始状态:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
value: localStorage.getItem('myValueInLocalStorage') || '',
};
}
componentDidUpdate() {
localStorage.setItem('myValueInLocalStorage', this.state.value);
}
onChange = event => {
this.setState({ value: event.target.value });
};
render() {
return (
<div>
<h1>Hello React ES6 Class Component!</h1>
<input
value={this.state.value}
type="text"
onChange={this.onChange}
/>
<p>{this.state.value}</p>
</div>
);
}
}
利用this.state、this.setState()和生命周期方法,React类组件中的状态管理和副作用可以写在一起。React类组件到现在依然在广泛使用,尽管后文即将介绍的React函数组件在现代React应用程序中得到了更广泛的应用,因为函数组件已经不逊于类组件了。
React高阶组件
React高阶组件(Higher-Order Components,简称 HOC)是一种React的高级设计模式,是替代Mixin的另一种在组件间复用逻辑的方法。如果你没听说过HOC,可以读一读我的另一篇入门文章:高阶组件(https://www.robinwieruch.de/gentle-introduction-higher-order-components/)。简单来说,高阶组件就是接收一个组件作为输入,然后输出另一个组件(并扩展其功能)的组件。我们利用前面的例子来看看,怎样才能将功能提取到可复用的高阶组件中。
const withLocalStorage = localStorageKey => Component =>
class WithLocalStorage extends React.Component {
constructor(props) {
super(props);
this.state = {
[localStorageKey]: localStorage.getItem(localStorageKey),
};
}
setLocalStorage = value => {
localStorage.setItem(localStorageKey, value);
};
render() {
return (
<Component
{...this.state}
{...this.props}
setLocalStorage={this.setLocalStorage}
/>
);
}
};
class App extends React.Component {
constructor(props) {
super(props);
this.state = { value: this.props['myValueInLocalStorage'] || '' };
}
componentDidUpdate() {
this.props.setLocalStorage(this.state.value);
}
onChange = event => {
this.setState({ value: event.target.value });
};
render() {
return (
<div>
<h1>
Hello React ES6 Class Component with Higher-Order Component!
</h1>
<input
value={this.state.value}
type="text"
onChange={this.onChange}
/>
<p>{this.state.value}</p>
</div>
);
}
}
const AppWithLocalStorage = withLocalStorage('myValueInLocalStorage')(App);
另一个高级React设计模式就是React Render Prop组件,通常代替React高阶组件使用。我不在给出这种抽象的例子,更多内容请查看网上的一些教程。
React高阶组件和React Render Prop组件在今天都在被广泛使用,尽管React函数组件和React钩子(后文会介绍)也许对于React组件的抽象更好。不过,高阶组件和Render Prop也可以用在函数组件上。
React函数组件
React函数组件等价于React类组件,但它表现为一个函数,而不是一个类。过去函数组件没有状态也无法使用副作用,因此它们被称为“无状态函数组件”,但自从React钩子出现后,函数组件就复活了。
React钩子给函数组件带来了状态和副作用。React不仅带有各种内置的钩子,还允许创建自定义的钩子。我们来看看前面的类组件的例子怎样改写成函数组件:
const App = () => {
const [value, setValue] = React.useState('');
const onChange = event => setValue(event.target.value);
return (
<div>
<h1>Hello React Function Component!</h1>
<input value={value} type="text" onChange={onChange} />
<p>{value}</p>
</div>
);
};
这段代码仅在输入框上演示了函数组件。由于要捕获输入框的值,就需要使用组件状态,因此这里用到了内置的React.useState钩子。
React钩子还可以在函数组件中实现副作用。一般来说,内置的useEffect钩子可以用来在任何props或state发生变化时执行一个函数:
const App = () => {
const [value, setValue] = React.useState(
localStorage.getItem('myValueInLocalStorage') || '',
);
React.useEffect(() => {
localStorage.setItem('myValueInLocalStorage', value);
}, [value]);
const onChange = event => setValue(event.target.value);
return (
<div>
<h1>Hello React Function Component!</h1>
<input value={value} type="text" onChange={onChange} />
<p>{value}</p>
</div>
);
};
这段代码演示了useEffect钩子的用法,每次状态中的输入框的值改变时,该钩子就会被执行。当提供给useEffect钩子的函数被执行时,它会利用最新的值更新local storage中的值。此外,函数组件的初始状态也可以使用useState钩子从local storage中读取。
最后一点,我们可以将讲个钩子提取出来,封装成一个自定义钩子,这样可以保证组件状态永远和local storage同步。它在最后会返回一个值和setter函数,供函数组件使用:
const useStateWithLocalStorage = localStorageKey => {
const [value, setValue] = React.useState(
localStorage.getItem(localStorageKey) || '',
);
React.useEffect(() => {
localStorage.setItem(localStorageKey, value);
}, [value]);
return [value, setValue];
};
const App = () => {
const [value, setValue] = useStateWithLocalStorage(
'myValueInLocalStorage',
);
const onChange = event => setValue(event.target.value);
return (
<div>
<h1>Hello React Function Component!</h1>
<input value={value} type="text" onChange={onChange} />
<p>{value}</p>
</div>
);
};
由于这段代码是从函数组件中提取出来的,它可以用于任何其他组件,以实现业务逻辑的复用。它与Mixin、高阶组件和Render Prop组件一样都是高级设计模式。但是需要指出的是,React的函数组件也可以用高阶组件和Render Prop组件来增强。
React函数组件、钩子和类组件是目前编写现代React应用程序的标准。但是,我坚信以后函数组件和钩子将取代类组件。届时,类组件也许只会出现在旧的应用程序和教程中,就像今天的 createClass组件和Mixin一样。高阶组件和Render Prop组件也同理,它们也会被钩子取代。
写在最后
所有React的组件在Pros的用法方面的理念都是一样的,都是将信息沿着组件树向下传递。但是,类组件和函数组件对于状态和副作用的用法是不同的,还有生命周期方法和钩子。
这篇文章介绍了所有不同种类的React组件及其用法,以及它们在历史中的位置。最后总结一下,现在使用类组件、函数组件和钩子、高阶组件和Render Prop组件等高级概念是完全没问题的。但是也应当了解到,旧的React应用程序和教程也会使用一些只有以前才使用的旧组件和设计模式。
原文:https://www.robinwieruch.de/react-component-types/
本文为 CSDN 翻译,如需转载,请注明来源出处。作者独立观点,不代表 CSDN 立场。
【完】
热 文 推 荐
System.out.println("点个在看吧!");
console.log("点个在看吧!");
print("点个在看吧!");
printf("点个在看吧!\n");
cout << "点个在看吧!" << endl;
Console.WriteLine("点个在看吧!");
Response.Write("点个在看吧!");
alert("点个在看吧!")
echo "点个在好看吧!"
点击阅读原文,输入关键词,即可搜索您想要的 CSDN 文章。
以上是关于一文详解 React 组件类型的主要内容,如果未能解决你的问题,请参考以下文章