上一章: 【React】后台开发学前端 04 - React组件02 - 函数组件
前言:
首先跟大家说医生抱歉,因为身体抱恙住院了几天,所以后台开发学前端停更了些许日子。再次也跟各位高程们提醒一句,身体是自己的,钱都是赚不完的。保命要紧!
另外,我们之前已经清楚了React基本的一些结构和构成react最重要的东西 -组件。今天,我们要接触到关于react组件的一些状态传递相关的知识,props和state
在进入学习之前,我们可以复习以下之前说起过的,React类组件是我们说到过的动态组件,他的动态在于能够根据传递的数据变化在某些页面加载的阶段来渲染一些“新东西”到页面。而函数组件是我们所谓的静态组件,他配合Hooks函数,也能做到动态这一说法,当然这也就离不开一些值传递,尤其是我们调用API获得的一些新data,如何通过动态渲染去更新页面。这就是跟我们的props和state有关的内容。
props和state
总的来说,props是组件对外的接口(也就是讲某个组件的值传递给外部另一个组件。或者说父组件把某个参数传递给子组件)state则是组件对内的接口(组件内部状态的改变)。
从之前学习到的知识来看,组件内部可以引用其他定义好的组件,这样使组件和组件之前形成了一个树形结构(组件树)。如果树的下层(内部组件)需要使用到上层(外部组件)的属性或者方法,上层组件就可以通过props 将这些属性或者方法传递给下层组件。而组件自身有时候也需要维护管理数据,这就需要用到state,而整个组件树就会根据props和state的配合,来计算出对应界面当前的UI应该怎样渲染。
props
都说了是两个组件之间的传递行为,我们看一个例子:
子组件的代码:
import React from 'react';
export default class child1 extends React.Component {
render() {
let { btnFunc, btnName } = this.props;
return (
<>
<button onClick={btnFunc}>{btnName}</button>
</>
);
}
}
- 定义了一个叫做Child1的子组件(记得组件名称首字母一定要大写,区别于其他html或者js代码,表示他本身是一个组件)
- 该组件接收了一个this.props的参数,从该参数中析构出两个参数,一个是btnFunc,就是从父组件传递过来的方法,一个叫做btnName,就是从父组件传过来的一个字符串。
- Child1组件内部做一个按钮,该按钮的名字就用传递过来的btnName,而该按钮的onClick事件就绑定传过来的btnFunc
父组件代码:
import React from 'react';
import Child1 from './Child1.jsx';
export default class Parent extends React.Component {
render() {
return (
<>
<Child1 btnFunc={() => { alert('OK') }} btnName="Test Buttion" />
</>
);
}
}
- 导入一个子组件,并且使用整个子组件,
- 在子组件上绑定一个btnFunc,该参数传入一个方法,该方法只是alert了一个OK
- 并绑定一个btnName,他的值是一个字符串
最后记得在App.js里面导入父组件,然后使用父组件:
import Parent from "./Parent.jsx";
export default function App() {
return (
<>
<Parent />
</>
);
}
最后我们能够在界面看到:
我们有一个叫做Test Button的按钮,整个Test Button就是父组件传递到子组件的btnName
然后点击按钮就能执行alert('OK')的方法,整个方法也是父组件传递给子组件的btnFunc
这个就是props的基本用法。而如上面的例子,我们在渲染页面的时候,我们发现父组件默认给子组件传递了一个叫做“props”的参数,而我们父组件在初始化子组件的时候绑定的那些东西(包括方法,参数等)都在props里面。我们把Child1组件的this打出来看看。
可以看到,组件Child1里面Ⅸ有一个props,而这个props里面就有我父组件初始化子组件的时候绑定到子组件上的自定义参数(一个btnFunc方法,一个btnName的字符串)。
其次我们能够看到他有一个refs,由此可见,我们初始化子组件的时候,自定义props的名称不能定义为refs。这里我们也能看到当前Child1组件内部还有个state,他的状态是null,表示无状态。
大家可以通过上面的代码看到,我是用类组件在实现props,而怎么使用函数组件来做props的传递呢。我们试试:
父组件改造很简单,就是把类组件的写法改为函数写法
import React from 'react';
import Child1 from './Child1.jsx';
export default function Parent() {
return (
<>
<Child1 btnFunc={() => { alert('OK') }} btnName='test2' />
</>
)
}
子组件就需要多点修改了, 因为函数组件没有this关键字,所以我们构造子组件的时候传入一个props对象,其实整个对象就是包含父组件需要传递参数的对象了。因此我们能够直接从传入的props对象中获取到btnFunc和btnName。最终展示的结果跟类组件一摸一样。
import React from 'react';
export default function Child1(props) {
console.log(props);
return (
<>
<button onClick={props.btnFunc}>{props.btnName}</button>
</>
);
}
我们再分别把this和props打印出来,可以看到this = undfined,因为说过了函数组件没有this
而props打印出来跟类组件的this里面拿出来的props一样。都包含传递过来的所有参数。
总结以下props,它是React一个比较核心的概念,因为我们不可能都做静态的组件,组件与组件之间肯定有这样那样的参数传递来影响彼此。比如说父组件的一些行为会影响自子组件的渲染,或者子组件的一些更新会同时更新父组件的一些页面渲染。这些都需要用的props来做参数传递,包括之后我们讲到组件自身状态(state)的修改会影响到相关的其他组件,这些也都是需要props将状态传递给其他组件。
好了,接下来我们来讲讲状态(state)
state
刚才说起了,state是组件对内的接口。也就是组件自身状态的改变。举个栗子,一个按钮组件,他默认的文本叫做“On”,当前组件状态可能就是”开启“ (On),而我可以通过给当前按钮绑定一个onClick的方法,按下后就能使当前组件的按钮状态变为“关闭” (Off),这就是组件内部状态变化了。
我们还是使用类组件来实现以下状态变化的功能。
import React from 'react';
export default class Button1 extends React.Component {
constructor(props) {
super(props);
this.state = {
switch: 'On'
};
}
switchBtn() {
if (this.state.switch === 'On') {
this.setState({ switch: 'Off' })
} else {
this.setState({ switch: 'On' })
}
}
render() {
return (
<>
<button onClick={this.switchBtn.bind(this)}>{this.state.switch}</button>
</>
);
}
}
如上代码:
- 构造函数初始化组件的时候设置一个状态,状态中包含一个switch的参数,默认为‘On’
- 写一个方法switchBtn,当前状态为‘On’的时候,重设状态为‘Off’,反之亦然。
- 绑定该方法为按钮事件,并且把switch状态设置为按钮状态。
最终我们能够在页面看到一个按钮,第一次渲染的时候为On,
当点击按钮的时候,状态改为Off,所以按钮的值变为Off
另外,如果在组件中不使用state去改变状态, 页面是没有办法渲染成功的,不信的可以自己试试不使用state去定义状态而是单独定义一个参数作为状态位,然后看看你的按钮是否改变了文字。测试代码如下:
import React from 'react';
export default class Button2 extends React.Component {
constructor(props) {
super(props);
this.switch = 'On';
}
switchBtn() {
if (this.switch === 'On') {
this.switch = 'Off';
} else {
this.switch = 'On';
}
console.log(this.switch);
}
render() {
return (
<>
<button onClick={this.switchBtn.bind(this)}>{this.switch}</button>
</>
);
}
}
组件内部直接定义了一个叫做switch的参数。并把它赋给了button的text。
然后button按钮事件绑定了一个方法,该方法直接修改switch的值。
结果如图:
可见,我们在每次改变switch状态的时候打印了他的值,他值是更改了的。但是页面上始终是”On“,表示页面没有正常渲染。所以我们在React中要让页面同时渲染, 就要使用state和setState方法
上面state相关的内容都是基于类组件的,正如我们所说,类组件是动态组件 ,支持各种状态的修改。也有自带的setState方法来修改状态。而函数组件自身是静态组件,并没有setState。但是它可以通过Hooks函数来实现动态的setState。整个我们在下一章中会提到。
而关于state,其实对于React来说相当重要,因为他本质上关乎到页面渲染。不是所有参数都可以定义为state。因为它不仅代表了一个UI组件呈现的完整状态的集合(就是所有会改变的状态),并且它还代表着整个UI组件的最小状态集(就是不会有任何多余的状态,也不会从今年跟其他状态或props计算得来的中间状态)。因此,看一个变量是否因该作为该组件的state。可以通过下面几点来判断:
- 变量是否通过props从父组件传递过来的,若是,则不能作为当前组件的状态。
- 变量是否在组件整个生命周期都保持不变, 若是,则不是一个状态。(状态就是变化态)
- 变量是否可以通过state或者其他props的数据计算得来,若是,不是当前组件的状态、
- 变量是否在组件的render方法中使用,如果不是,则不能作为一个状态。
- 并不是组件中用到的所有变量都是组件状态,当存在多个组件共同依赖同一个状态的话,一版就会将该状态“向上提升”到公共父组件中定义。
总结
最后总结以下props和state的一些基本需要知道的知识点:
- props是组件之间做参数传递用的,而state是组件内部管理自己状态的。
- props是不可修改的,因为如果props可以修改,那么组件的行为将不可预测。理论上父组件传递相同的props给子组件,子组件就应该固定渲染出相同的样式。
- 函数组件自身是没有props的,props通过子组件的参数获取。而类组件可以通过this.props获取到相关怕props。
- state是在组件内部创建的,而且只有constructor方法能够初始化state
- state是可以被修改变化的,但是不可以直接修改,需要通过setState来修改,而每次的修改过程都是异步执行的。
- 如果多个组件都需要使用同一个state,那么该state要“向上提升”到父组件层级来定义。这种情况叫做“状态提升”