React:类组件与函数组件
刚开始使用 React 的时候,我主要使用函数组件,尤其是因为我读到过有人说 Class 组件老旧过时。但当我开始专业地使用 React 时,我意识到我错了。Class 组件现在依然非常流行。
因此,我决定写一种类组件和函数组件之间的比较,以便更好地理解它们的异同。
目录
类组件
这是一个利用的类组件state
,props
如下render
所示:
class Hello extends React.Component {
constructor(props) {
super(props);
this.state = {
name: props.name
};
}
render() {
return <h1>Hello, {this.state.name}</h1>;
}
}
// Render
ReactDOM.render(
Hello,
document.getElementById('root')
);
您可以在相关来源中找到有关此内容的更多信息:
渲染
假设 <div>
你的 HTML 文件中有这样一段内容:
<div id="root"></div>
我们可以用如下方式element
来代替 来渲染:div
root
id
const element = <h1>Hello, world</h1>;
ReactDOM.render(element, document.getElementById('root'));
关于 React 组件,我们通常会导出一个组件并在另一个文件中使用它:
Hello.jsx
import React, { Component } from 'react';
class Hello extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
export default Hello;
main.js
import React from 'react';
import ReactDOM from 'react-dom';
import Hello from './app/Hello.jsx';
ReactDOM.render(<Hello />, document.getElementById('root'));
这就是类组件在 Web 浏览器上的呈现方式。
现在,渲染和安装之间存在差异,Brad Westfall对此进行了很好的总结:
“渲染”是指任何时候调用函数组件(或基于类的渲染方法),并返回一组用于创建 DOM 的指令。
“挂载”是指 React 首次“渲染”组件,并根据这些指令实际构建初始 DOM。
状态
状态是一个 JavaScript 对象,包含有关组件当前状况的信息。
要初始化类组件状态,我们需要使用constructor
:
class Hello extends React.Component {
constructor() {
this.state = {
endOfMessage: '!'
};
}
render() {
return <h1>Hello, {this.props.name} {this.state.endOfMessage}</h1>;
}
}
关于此内容的相关资料:
注意:我们不应该直接修改状态,因为它不会触发组件的重新渲染:
this.state.comment = 'Hello'; // Don't do this
相反,我们应该使用以下setState()
方法:
this.setState({comment: 'Hello'});
如果我们的当前状态依赖于前一个状态,并且setState
是异步的,我们应该考虑前一个状态:
this.setState(function(prevState, prevProps) {
return {
counter: prevState.counter + prevProps.increment
};
});
关于此内容的相关资料:
常见的陷阱
如果我们需要设置具有嵌套对象的状态,则我们应该扩展该对象中的所有嵌套级别:
this.setState(prevState => ({
...prevState,
someProperty: {
...prevState.someProperty,
someOtherProperty: {
...prevState.someProperty.someOtherProperty,
anotherProperty: {
...prevState.someProperty.someOtherProperty.anotherProperty,
flag: false
}
}
}
}))
[immutability-helper](https://github.com/kolodny/immutability-helper)
这可能会变得麻烦,因此建议使用该包。
关于此内容的相关资料:
在我了解之前,我以为设置新的对象属性总是会保留未设置的属性,但对于嵌套对象来说并非如此(这有点合乎逻辑,因为我会用另一个对象覆盖一个对象)。这种情况发生在我之前展开对象,然后修改它的某个属性时:
> b = {item1: 'a', item2: {subItem1: 'y', subItem2: 'z'}}
//-> { item1: 'a', item2: {subItem1: 'y', subItem2: 'z'}}
> b.item2 = {...b.item2, subItem1: 'modified'}
//-> { subItem1: 'modified', subItem2: 'z' }
> b
//-> { item1: 'a', item2: { subItem1: 'modified', subItem2: 'z' } }
> b.item2 = {subItem1: 'modified'} // Not OK
//-> { subItem1: 'modified' }
> b
//-> { item1: 'a', item2: { subItem1: 'modified' } }
但是,当我们有嵌套对象时,我们需要使用多个嵌套展开,这会导致代码重复。这时 immutability-helper 就派上用场了。
您可以在此处找到有关此内容的更多信息。
道具
如果我们想要访问props
,constructor
我们需要使用以下命令调用父类构造函数super(props)
:
class Button extends React.Component {
constructor(props) {
super(props);
console.log(props);
console.log(this.props);
}
// ...
}
关于此内容的相关资料:
请记住,使用props
设置初始状态 是 React 的反模式。过去,我们可以使用该componentWillReceiveProps
方法来设置初始状态,但现在它已被弃用。
class Hello extends React.Component {
constructor(props) {
super(props);
this.state = {
property: this.props.name, // Not recommended, but OK if it's just used as seed data.
};
}
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
如果我们明确表示仅用作组件内部控制状态的种子数据,那么使用props
它来初始化并不是反模式。state
prop
关于此内容的相关资料:
- https://sentry.io/answers/using-props-to-initialize-state/
- https://reactjs.org/docs/react-component.html#unsafe_componentwillreceiveprops
- https://medium.com/@justintulk/react-anti-patterns-props-in-initial-state-28687846cc2e
生命周期方法
类组件没有hooks
;它们有生命周期方法。
render()
componentDidMount()
componentDidUpdate()
componentWillUnmount()
shouldComponentUpdate()
static getDerivedStateFromProps()
getSnapshotBeforeUpdate()
您可以在这里了解有关生命周期方法的更多信息:
- https://programmingwithmosh.com/javascript/react-lifecycle-methods/
- https://reactjs.org/docs/state-and-lifecycle.html
函数组件
函数组件使用props
、state
和 的方式如下render
:
function Welcome(props) {
const [timeOfDay, setTimeOfDay] = useState('morning');
return <h1>Hello, {props.name}, good {timeOfDay}</h1>;
}
// or
const Welcome = (props) => {
const [timeOfDay, setTimeOfDay] = useState('morning');
return <h1>Hello, {props.name}, good {timeOfDay}</h1>;
}
// Render
const element = <Welcome name="Sara" />;
ReactDOM.render(
element,
document.getElementById('root')
);
渲染
渲染函数组件的方式与类组件相同:
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
const element = <Welcome name="Sara" />;
ReactDOM.render(
element,
document.getElementById('root')
);
来源:
状态
说到状态,函数组件与类组件有很大不同。我们需要定义一个包含两个主要元素的数组:状态值和用于更新状态的函数。然后,我们需要将钩子赋值useState
给该数组,并在过程中初始化状态:
import React, { useState } from 'react';
function Example() {
// Declare a new state variable, which we'll call "count"
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
钩子是函数组件允许我们以 与类组件类似的方式 useState
使用组件的方式。state
this.state
记住:函数组件使用hooks。根据官方文档:
什么是 Hook? Hook 是一种特殊的函数,可以让你“钩住” React 的功能。例如,
useState
Hook 可以让你向函数组件添加 React 状态。我们稍后会学习其他 Hook。什么时候应该使用 Hook? 如果你编写了一个函数组件,并意识到需要为其添加一些状态,以前你必须将其转换为类。现在,你可以在现有的函数组件中使用 Hook。
要读取函数组件的状态,我们可以使用useState
在函数声明中使用时定义的变量(count
在我们的示例中)。
<p>You clicked {count} times</p>
在类组件中,我们必须做这样的事情:
<p>You clicked {this.state.count} times</p>
setCount
每次我们需要更新状态时,我们都应该使用新状态的值调用我们定义的函数(在本例中)。
<button onClick={() => setCount(count + 1)}>
Click me
</button>
同时,在类组件中我们使用this
关键字,后跟state
要更新的属性:
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
Click me
</button>
资料来源:
道具
最后,props
在函数组件中使用非常简单:我们只需将它们作为组件参数传递:
function Avatar(props) {
return (
<img className="Avatar"
src={props.user.avatarUrl}
alt={props.user.name}
/>
);
}
来源:
结论
决定使用类组件还是函数组件取决于具体情况。据我所知,专业环境会将类组件用作“主要”组件,而将函数组件用作较小的特定组件。不过,根据你的项目情况,情况可能并非如此。
我很想看到在特定情况下使用类和函数组件的示例,所以不要羞于在评论部分分享它们。
🗞️新闻通讯 - 如果您想了解我的最新文章和有趣的软件开发内容,请订阅我的新闻通讯。
🐦TWITTER——在Twitter上关注 我。
文章来源:https://dev.to/colocodes/react-class-components-vs-function-components-23m6