你可以先将它想的简单一点,首先它是一个对象,里面保存了当前组件的数据(状态),我们可以将它理解为这样的一个数据载体
就是因为我们在React开发中应减少DOM操作,所以才需要去控制状态来间接改变DOM,为什么是间接的呢?因为真正改变DOM节点或者说页面数据这个步骤不需要我们自己去执行,只需要告诉React我想怎么做就可以了
说的正经点,状态就是组件描述某种显示情况的数据,由组件自己设置和更改,也就是说由组件自己维护,使用状态的目的就是为了在不同的状态下使组件的显示不同,也就是数据更改(不是我们直接修改state中的数据,而是使用setState方法让React为我们更改真实数据),对于DOM来说操作,React其实将它做出了一个备份,也就是虚拟DOM,使用diff算法比较之后,将新的状态更新到视图(页面)上,或者说是更新到DOM树
关于为什么不能直接修改state中的属性(与Vue进行比较):
react没有和Vue一样去底层拦截数据,感知不到数据的改变,在Vue中,data属性是利用Object.defineProperty处理过的,更改data的数据会触发数据的getter和setter,但是react中没有做过这样的处理,如果直接更改的话,react是无法得知的,所以需要使用特殊的方法更改状态setState
在类中直接书写一个state对象:
state = {
tets:'React状态对象'
}
或者书写到constructor()中,不过需要注意的是,必须带上super() :
constructor() {
super() // 继承
this.state = {
my_state: true,
user_name: "Anna",
}
}
同样还是在类中:
this.setState({
my_state: false,
user_name: "Buun",
})
我们把剩下的操作交给React处理吧,我们只需要通过this.setState告知React我们需要怎么改就可以了
当然,我们的关注点应该在这里:
setState方法第二参数的妙用(同步和异步)首先setState方法第二参数是一个回调函数,当真实数据更改完毕之后,React会去执行这个回调
this.setState(
{
message: '只有真实数据被更改,下面的回调才会执行',
},
() => {
console.log(this.state.message);
}
);
这使我整个人都很懵啊😵,这个有什么用呢?难道下面这样我就不能输出修改后的值了吗?
this.setState(
{
message: '只有真实数据被更改,下面的回调才会执行',
}
);
console.log(this.state.message);
一点没错,就是因为这样打印出来的就旧值,而不是修改后的值
当然,数据已经被修改了,这个是毋庸置疑的
这就是因为setState是一个神奇的方法:
setState方法处在同步逻辑中是异步更新的,主线程空闲之后,才会执行更新状态,更新真实DOM,所以console.log()会先执行,打印出旧值setState方法处在异步逻辑中是同步更新的,比如在settimeout中: setTimeout(() => {
this.setState({
message: '新值被打印出来了',
});
}, 0);
console.log(this.state.message);
这就是因为settimeout本身就是一个异步执行上下文,那么setState方法处在异步逻辑中是同步更新的
关于状态我只是相对于类阐述的,函数组件本身是没有状态的,所以必须使用hooks来处理,这个我们之后再说
但是一说起属性,那么函数式组件可是天生丽质呀🥰
不过不要心急,我们还是先相对于类组件来探讨
类组件的属性传递如果你学过Vue,那么你一定知道这个属性:props,并且无论在Vue还是React中,他都只是一个只读属性,我们往下看:
首先我们需要学会怎么使用它,慢慢的再来往深说:
父组件上通过key = value 书写属性,相对的子组件通过this.props获取属性,可以提高组件的复用性
import React, { Component } from 'react';
import Navbar from './Navbar/props';
export default class App extends Component {
render() {
return (
<section>
<div>
<h2>首页</h2>
<Navbar title="首页" data_show={false}></Navbar>
</div>
<div>
<h2>内容</h2>
<Navbar title="内容" data_show={true}></Navbar>
</div>
<div>
<h2>我的</h2>
<Navbar title="我的" data_show={true
}></Navbar>
</div>
</section>
);
}
}
这个真的很好理解,属性通过属性来传递,哈哈哈😆
紧接着我们来看看我们的子组件:
import React, { Component } from 'react';
export default class Navbar extends Component {
render() {
const { title, data_show } = this.props;
return (
<section>
{data_show && <button>返回</button>}
navbar-{title}
<button>菜单</button>
</section>
);
}
}
让我们看看效果怎么样,等等,我觉得有些地方需要修改一下:

每一个都需要写,我比较懒,我决定设置一个默认值(在子组件中):
// 写在类的外面
Navbar.defaultProps = {
data_show: true,
};
// 或者写在类的里面(使用静态属性static)
static defaultProps = {
data_show: true,
};
再等等,需要传递的属性这么多,写在结构中也不太好看,不如我们写成一个对象吧:
const obj = {
title: '测试',
leftshow: false,
};
// 在父组件中:
<div>
<h2>测试</h2>
<Navbar title={obj.title} data_show={obj.leftshow}></Navbar>
</div>
// 你骗谁呢?这不是一样的吗??,好像没有简单呀
// 如果我们使用ES6的扩展运算符呢?
<div>
<h2>测试</h2>
<Navbar {...obj}></Navbar>
</div>
不过好像也用处不大,算了,就当闹着玩吧😭
验证属性
这一步可以说是一定要做的,验证父组件传过来的值是否为指定数据类型:
// 导入验证属性模块
import propsTypes from 'prop-types';
// 在类中(在类中定义对象属性加上静态属性static就成为一个类属性了)
static propTypes = {
title: propsTypes.string,
data_show: propsTypes.bool,
};
这避免了不必要的错误
好了,改的够多了,我们来看看效果:

好吧 我承认我做的一点也不好看🤐
// 函数式组件天生支持传递属性
import React, { Component } from 'react';
import Navbar from './Navbar/props';
import Sidebar from '../Sidebar/Sidebar';
export default class App extends Component {
render() {
return (
<section>
{/* 类组件 */}
<Navbar title="导航"></Navbar>
{/* 函数式组件 */}
<Sidebar bgcl="red" fontWeight="bold" position="right"></Sidebar>
</section>
);
}
}
父组件的传递方式还是一样的,我们来看看子组件(函数式组件)是怎么做的
// Sidebar组件是一个函数式组件
export default function Sidebar(props) {
// 使用一个参数接收父组件传过来的props属性,就这么简单
// 然后解构赋值
const { fontWeight, bgcl, position } = props;
const objRight = {
right: 0,
};
const objLeft = {
left: 0,
};
const objPosition = {
position: 'fixed',
fontWeight: fontWeight,
backgroundColor: bgcl,
};
// console.log(bgcl);
const styleObj =
position === 'right'
? { ...objRight, ...objPosition }
: { ...objLeft, ...objPosition };
return (
<section style={styleObj}>
<ul>
<li>你好</li>
<li>你好</li>
</ul>
</section>
);
}

效果依旧是那么的好,样式依旧是那么的差😅😅
首先它们都是纯JavaScript对象,都会触发render更新,都具有确定性(状态/属性相同,结果相同)
其次是不同点:
为什么不能修改props属性值?实践出真知,我们看看这段代码:
import React, { Component } from 'react';
class Chlid extends Component {
render() {
return (
<section>
<button
onClick={() => {
this.props.text = '不能再子组件修改只读属性😥';
}}
>
修改
</button>
Chlid -- {this.props.text}
</section>
);
}
}
export default class App extends Component {
state = {
text: 'giao',
};
render() {
return (
<section>
<Chlid text={this.state.text}></Chlid>
<p>--------------------------------</p>
<button
onClick={() => {
this.setState(
{
// 修改完毕之后,子组件的props属性也被相应修改了
// 所以说属性可以由父组件修改,状态不能
text: '修改好了',
},
() => {
console.log('你好😃');
}
);
}}
>
点我修改内容
</button>
</section>
);
}
}


state的作用是用于组件保存、控制、修改自己的可变状态,state在组件内部进行初始化,可以被组件自身修改,而外部不能访问也不能修改
你可以认为state是一个局部的,只能被组件自身控制的数据源,state中状态可以通过 this.setState 方法进行更新,setState会导致组件的重新渲染
props属性的主要作用是让使用该组件的父组件可以传入参数来配置该组件,组件内部无法控制也无法修改,除非外部组件主动传入新的props,否则组件的props永远保持不变,他就是一个只读属性
没有state的组件叫做无状态组件(stateless cmomponent),设置了state的组件叫做有状态组件(stateful component),因为状态会带来管理的复杂性,我们尽量多写无状态组件,尽量少些有状态组件,这样会降低代码维护的难度,也会在一定程度上增强组件的可复用性