2022-11-24 11:15:20 +08:00

344 lines
6.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 组件状态与数据传递
本章主要包含以下知识点:
- 组件状态
- *props*
- *props* 验证
- 状态提升
## 组件状态
早期类组件被称之为有状态组件,就是因为在类组件中能够维护组件数据。
```js
class 类名 extends React.Component{
constructor(){
super();
// 设置组件自身的数据状态
this.state = {
xxx : xxx
}
}
render(){
return (
// 通过 {this.state.xxx} 来获取状态数据
)
}
}
// 或者
class 类名 extends React.Component{
state = {
xxx : xxx
}
render(){
return (
// 通过 {this.state.xxx} 来获取状态数据
)
}
}
```
不要直接去修改状态值,而是应该通过 *setState* 方法修改组件的 *state* 状态数据。
```js
this.setState({
xxx: 新值
})
```
*setState*,它对状态的改变,**可能**是异步的。
> 如果改变状态的代码处于某个 *HTML* 元素的事件中,则其是异步的,否则是同步
如果在事件处理函数里面想拿到 *setState* 执行后的数据,可以提前使用一个变量来存储计算结果,或者使用 *setState* 的第二个参数,它是一个函数,这个函数会在 *state* 更新后被调用。
最佳实践:
1. 把所有的 *setState* 当作是异步的
2. 永远不要信任 *setState* 调用之后的状态
3. 如果要使用改变之后的状态,需要使用回调函数(*setState* 的第二个参数)
4. 如果新的状态要根据之前的状态进行运算,使用函数的方式改变状态(*setState* 的第一个函数)
*React* 会对异步的 *setState* 进行优化,将多次 *setState* 进行合并(将多次状态改变完成后,再统一对 *state* 进行改变,然后触发 *render*
## *props*
*Vue* 一样,在 *React* 中组件会存在层级关系,那么自然会涉及到组件之间进行数据的传递。
如果是父组件向子组件传递数据,则使用 *props*
如果是函数组件,*props* 作为函数的一个参数传入:
```react
function 组件名(props) {
return (
// 一段 JSX
// 通过 props.xxx 获取传入的值
<div>
<p>姓名{props.name}</p>
<p>年龄{props.age}</p>
<p>性别{props.gender}</p>
</div>
);
}
```
如果是类组件,则需要在 *constructor* 中将 *props* 通过 *super* 传递给父类,然后通过 *this.props* 的方式来获取传入的值:
```react
class 组件名 extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
// 一段 JSX
// 通过 this.props.xxx 获取传入的值
<div>
<p>姓名{this.props.name}</p>
<p>年龄{this.props.age}</p>
<p>性别{this.props.gender}</p>
</div>
);
}
}
```
通过 *props.children*,可以实现类似于 *Vue* 中插槽的功能,例如:
```react
class 组件B extends React.Component{
constructor(props){
super(props);
}
render(){
return (
<div>
{this.props.children}
</div>
);
}
}
class 组件A extends React.Component{
constructor(props){
super(props);
}
render(){
return (
<组件B>
<p>Hello, React</p>
<p>Hello, Redux</p>
<p>Hello, Facebook</p>
<p>Hello, Google</p>
</组件B>
);
}
}
```
## *props* 验证
*Vue* 中,可以对传入的 *props* 设置默认值,以及验证 *props* 的有效性,在 *React* 中,针对 *props* 也可以做这些事。
通过 *defaultprops* 就可以对 *props* 设置默认值。
```react
function Greeting(props) {
const { name, age, gender } = props;
return (
<div>
<p>姓名{name}</p>
<p>年龄{age}</p>
<p>性别{gender}</p>
</div>
);
}
// 设置默认的 props 值,当组件没有传值时会使用默认值
Greeting.defaultProps = {
name : 'xiejie',
age : 18,
gender : 'male'
};
```
```react
class Greeting extends React.Component {
constructor(props) {
super(props);
}
// 设置默认的 defaultProps 属性值
static defaultProps = {
name : "xiejie",
age : 18,
gender : 'male'
}
render() {
return (
<div>
<p>姓名{this.props.name}</p>
<p>姓名{this.props.age}</p>
<p>姓名{this.props.gender}</p>
</div>
);
}
}
// 或者
Greeting.defaultProps = {
name : "xiejie",
age : 18,
gender : 'male'
}
```
关于 *props* 的类型检查,从 *React v15.5* 版本开始,移入到了 [`prop-types` 库](https://www.npmjs.com/package/prop-types) 。
```react
import PropTypes from 'prop-types';
class Greeting extends React.Component {
render() {
return (
<h1>Hello, {this.props.name}</h1>
);
}
}
Greeting.propTypes = {
name: PropTypes.string
};
```
```react
import PropTypes from 'prop-types'
function HelloWorldComponent({ name }) {
return (
<div>Hello, {name}</div>
)
}
HelloWorldComponent.propTypes = {
name: PropTypes.string
}
export default HelloWorldComponent
```
## 状态提升
*Vue* 中,父传子通过 *props*,子传父通过触发自定义事件。
*React* 中,如果子组件需要向父组件传递数据,同样是通过触发父组件传递给子组件的事件来进行传递。
这在官网中被称之为“状态提升”:*https://zh-hans.reactjs.org/docs/lifting-state-up.html*
汇率转换案例:
父组件
```react
import React from "react";
import Money from "./component/Money";
// 类组件
class App extends React.Component {
state = {
dollar: "",
rmb: ""
}
transformToRMB = (value) => {
if (parseFloat(value) || value === "" || parseFloat(value) === 0) {
this.setState({
dollar: value,
rmb: value === "" ? "" : (value * 7.3255).toFixed(2)
})
} else {
alert("请输入数字");
}
}
transformToDollar = (value) => {
if (parseFloat(value) || value === "" || parseFloat(value) === 0) {
this.setState({
dollar: value === "" ? "" : (value * 0.1365).toFixed(2),
rmb: value
})
} else {
alert("请输入数字");
}
}
render() {
return (
<div>
<Money text="美元" money={this.state.dollar} transform={this.transformToRMB} />
<Money text="人民币" money={this.state.rmb} transform={this.transformToDollar} />
</div>
)
}
}
export default App;
```
子组件
```react
import React from 'react';
function Money(props) {
function handleChange(e){
// 在子组件中,要做的事情很简单,将用户数据的值,传递给父组件
// 让父组件来进行修改
props.transform(e.target.value);
}
return (
<fieldset>
<legend>{props.text}</legend>
<input type="text" value={props.money} onChange={handleChange}/>
</fieldset>
);
}
export default Money;
```
---
-*EOF*-