本章主要包含以下知识点:
- 组件状态
- props
- props 验证
- 状态提升
组件状态
早期类组件被称之为有状态组件,就是因为在类组件中能够维护组件数据。
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 状态数据。
this.setState({
xxx: 新值
})
setState,它对状态的改变,可能是异步的。
如果改变状态的代码处于某个 HTML 元素的事件中,则其是异步的,否则是同步
如果在事件处理函数里面想拿到 setState 执行后的数据,可以提前使用一个变量来存储计算结果,或者使用 setState 的第二个参数,它是一个函数,这个函数会在 state 更新后被调用。
下面这这个很牛!!
最佳实践:
- 把所有的 setState 当作是异步的
- 永远不要信任 setState 调用之后的状态
- 如果要使用改变之后的状态,需要使用回调函数(setState 的第二个参数)(会回调地狱)
// 解决回调地狱
// 要解决这个问题,可以让 setState() 接收一个函数而不是一个对象。这个函数用上一个 state 作为第一个参数,将此次更新被应用时的 props 做为第二个参数:
this.setState((state, props) => ({
counter: state.counter + props.increment
}));
// 也就是我们说的第四点
// state拿到永远是上一次改变后的值
- 如果新的状态要根据之前的状态进行运算,使用函数的方式改变状态(setState 的第一个函数)
5、React 会对异步的 setState 进行优化,将多次 setState 进行合并(将多次状态改变完成后,再统一对 state 进行改变,然后触发 render)
state = {
num: 1
}
this.setState((state) => ({
num: state.num + 1
}), () => {
// 猜猜这里会打印几?????????
// 打印的是4!!!!!
// 这就是react的性能处理,汇会把多次计算合并统一渲染
console.log(this.state.num, 'num');
});
this.setState((state) => ({
num: state.num + 1
}));
this.setState((state) => ({
num: state.num + 1
}));
props
和 Vue 一样,在 React 中组件会存在层级关系,那么自然会涉及到组件之间进行数据的传递。
如果是父组件向子组件传递数据,则使用 props 。
如果是函数组件,props 作为函数的一个参数传入:
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 的方式来获取传入的值:
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 中插槽的功能,例如:
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 设置默认值。
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'
};
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库 。
import PropTypes from 'prop-types';
class Greeting extends React.Component {
render() {
return (
<h1>Hello, {this.props.name}</h1>
);
}
}
Greeting.defaultProps = {
name : 'xiejie',
age : 18,
gender : 'male'
};
Greeting.propTypes = {
name: PropTypes.string,
age: PropTypes.number,
gender: PropTypes.string
};
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
汇率转换案例:
父组件
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;
子组件
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;