【学习笔记】前端学习-React18-第三章 Props与state
三 Props与state
3.1 Props (属性对象)
是父组件传递数据给子组件的一种机制,Props是只读的子组件不应该修改他们,而是应该由父组件管理和维护。
Props是子组件可接收的所有属性对象,他是一个对象,属性名由传递给子组件的属性名称所决定。
<ComponentName propName1={propValue1} propName1={propValue1}/>
如上述案例:
将名称为propName1、propName2的属性传递给ComponentName组件。
3.1.1 实战
可以在组件内通过调用组件的defaultProps 属性为组件提供一个默认的Props,并通过propTypes设置Props的类型。
在使用props验证之前请先安装 prop-types依赖
yarn add prop-types
// 子组件 src/App.jsx
import React from "react";
import PropTypes from "prop-types";
class App extends React.Component {
render() {
return <>
<div>
{this.props.name},今年{this.props.age}岁
</div>
</>
}
}
App.propTypes = {
name: PropTypes.string,
age: PropTypes.number,
}
App.defaultProps = {
name: "Mark·虎",
age: 29
}
export default App;
接下来调整父组件,使其给子组件传递数据。
// 父组件 src/main.jsx
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'
ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<App name={"虎克"}/>
</React.StrictMode>
)
// 父组件 src/main.jsx
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'
ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<App name={4}/>
</React.StrictMode>
)
当我们给Props的name属性传递一个数值类型的值时,控制台会有警告信息,但是页面显示是会正常显示的。
Warning: Failed prop type: Invalid prop name
of type number
supplied to App
, expected string
.
通过在render函数中使用this.props.name来获取props的name值,在这个案例里已经展示了子组件默认值、接收多个Prop、与数据类型验证。
同样我们可以将子组件改写为函数式:
// 子组件 src/App.jsx
import PropTypes from "prop-types";
function App(props) {
return <>
<div>{props.name},今年{props.age}岁</div>
</>
}
App.propTypes = {
name: PropTypes.string,
age: PropTypes.number,
}
App.defaultProps = {
name: "Mark·虎",
age: 29,
}
export default App;
值得注意的是:虽然函数式中也可以用defaultProps属性来指定默认值但是由于更加推崇TypeScript在函数式组件中defaultProps属性已被标记为过时,会出现:Support for defaultProps will be removed from function components in a future major release. Use JavaScript default parameters instead.的错误提示。
3.2 state(状态)
状态是组件的私有属性,可以由组件维护,通过和用户交互达到变化的目的。在React 16.8之前函数式组件是无法使用状态的,因此被称为无状态组件,React 16.8以后Hook可以使函数式组件使用状态,从而组件不在区分无状态和有状态,而达到两种方法的统一,但是我更加推崇类组件,应为这是符合ES6规范的组件编写。
3.2.1 实战
// 子组件 src/App.jsx
import React from "react";
import PropTypes from "prop-types";
class App extends React.Component {
constructor(props) {
super(props);
this.state ={
name: props.name,
age: props.age
}
}
onChange = () => {
this.setState(prevState => ({age: prevState.age + 1}));
}
render() {
return <>
<div>
{this.state.name},今年{this.state.age}岁
<button onClick={this.onChange} >加一岁</button>
</div>
</>
}
}
App.propTypes = {
name: PropTypes.string,
age: PropTypes.number,
}
App.defaultProps = {
name: "Mark·虎",
age: 29
}
export default App;
通过点击按钮可以实现对状态state的改变。
你会发现这里多了一个constructor(),这个是ES6规范中类的构造函数,在constructor中接收props并通过super传递给父类,在constructor中先用this.state 构造一个初始的state,注意一定要先在这里对所有state进行初始化才可以在render中使用。
在onChange中通过this.setState改变state,这里参数是一个匿名函数,参数会传入state对象,在过程中也改变state的属性值。
函数式写法暂不在这里写,函数式在Hook章节中写。
3.3 props与state的高级用法
在进入这部分之前再次说明,state是组件私有的它由组件内部产生,不向外导出,只能组件内部使用,组件自身的state变化不影响其他组件,哪怕是相同的组件被调用多次,每个组件之间的state也是隔离的。这一点非常重要!!!!!
3.3.1 Props的解构
如果你不知道解构是什么,还请了解ES6语法。
延用3.1.1 的案例
function App(props) {
return <>
<div>{props.name},今年{props.age}岁</div>
</>
}
这里我我们接收的是props的对象在调用的时候分别调用他们的属性但是我们可以在接收时就把接收name与age
改为:
// 子组件 src/App.jsx
import PropTypes from "prop-types";
function App({name,age}) {
return <>
<div>{name},今年{age}岁</div>
</>
}
App.propTypes = {
name: PropTypes.string,
age: PropTypes.number,
}
App.defaultProps = {
name: "Mark·虎",
age: 29,
}
export default App;
3.3.2 Props的展开
延用 3.2.1的案例
constructor(props) {
super(props);
this.state ={
name: props.name,
age: props.age
}
}
改为
constructor(props) {
super(props);
this.state ={
...props
}
}
3.3.3 子组件state改变通过props传递给子组件
新增一个src/Message.jsx的组件
// 子组件 src/Message.jsx
import React from "react";
import PropTypes from "prop-types";
class Message extends React.Component {
constructor(props) {
super(props);
}
render() {
return <div>{this.props.name},今年{this.props.age}岁</div>;
}
}
Message.defaultProps = {
name: "Mark·虎",
age: 29
}
Message.propTypes = {
name: PropTypes.string,
age: PropTypes.number
}
export default Message;
修改子组件src/App.jsx
// 子组件 src/App.jsx
import React from "react";
import PropTypes from "prop-types";
import Message from "./Message.jsx";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
age: 0
};
}
onChange = () => {
this.setState(prevState => ({age: prevState.age + 1}));
}
render() {
return <>
<div>
<Message name={"虎"} age={this.state.age}></Message>
<button onClick={this.onChange} >加一岁</button>
</div>
</>
}
}
App.propTypes = {
name: PropTypes.string,
age: PropTypes.number,
}
App.defaultProps = {
name: "Mark·虎",
age: 29
}
export default App;
这时候点击页面上的加一岁会引发App.jsx组件的state变化,由于通过Props传递给了Message.jsx,因此会使得Message.jsx中的Props发生变化,因为State发生变化父组件会重新渲染。这就是父子组件通讯的父组件传递子组件,
3.3.4 传递回调函数作为 Props
在子组件src/Message.jsx定义一个方法调用Props中传递的方法,并绑定到当前按钮的点击事件上。
// 子组件 src/Message.jsx
import React from "react";
import {func} from "prop-types";
class Message extends React.Component {
constructor(props) {
super(props);
}
sendMessage= () => {
this.props.onMessage("欢迎你");
}
render() {
return <button onClick= {this.sendMessage}>请点击我</button>;
}
}
Message.propTypes = {
onMessage: func
}
export default Message;
父组件src/App.jsx 编写一个方法,通过接收一个参数改变自身的state
// 子组件 src/App.jsx
import React from "react";
import PropTypes from "prop-types";
import Message from "./Message.jsx";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
message: ''
};
}
onMessage = (meg) => {
this.setState({message: meg});
}
render() {
return <>
<div>
<Message onMessage= {this.onMessage}></Message>
<div>Message: {this.state.message}</div>
</div>
</>
}
}
App.propTypes = {
message: PropTypes.string,
}
App.defaultProps = {
message: "",
}
export default App;
点击页面的“请点击我”的按钮后Message:后面会显示出“欢迎你”