React 内部数据 state v.s. 外部数据 props

简介: React 内部数据 state v.s. 外部数据 props

1、添加外部数据 props

和 Vue 类似,React 的组件之间也需要进行传值。父组件需要在 子组件的占位符 上通过 赋值 的形式将值传递给子组件,子组件通过 props 属性拿到父组件传递的值,但是函数组件和类组件在使用外部数据时会有所不同:


类组件  可在其 constructor 接收父组件传递来的数据,直接通过 this.props.xxx 读取 父组件传递给子组件的 xxx 信息。


函数组件 可在其 参数 中接收到父组件传递来的数据,然后直接 读取 参数 props.xxx 即可。


在组件传值时,无论是使用函数组件还是类组件,如果是字符串用 " ",如果是变量用:{ }。下面我们看一个例子:

import React from "react";
import ReactDOM from "react-dom";
function App() {
  let n = 1;
  return (
    <div className="App">
      爸爸
      <Son messageForSon="儿子你好" />    <!--如果给子组件需要传递变量:messageForSon={n}-->
    </div>                               <!--如果给子组件需要传递函数:onClick={this.onClick}-->
  );                                     <!--外部数据被包装为一个对象{messageForSon:'',...}-->
}
class Son extends React.Component {      <!--类组件-->
  constructor(props){    <!--如果没有额外的代码,这三行可省略,不写也可以拿到外部数据-->
    super(props);        <!--如果没有额外的代码,这三行可省略,不写也可以拿到外部数据-->
  }                      <!--这样做的效果就是,this.props就是外部数据的地址了-->
  render() {
    return (
      <div className="Son">
        我是儿子,我爸对我说「{this.props.messageForSon}」<!--this.props.xxx 获取外部数据-->
        <Grandson messageForGrandson="孙贼你好" />       <!--如果给子组件需要传递变量:messageForSon={n}-->
      </div>
    );
  }
}
const Grandson = props => {    <!--函数组件可以接收外部传来的 props,这里的props是形参-->
  return (
    <div className="Grandson">
      我是孙子,我爸对我说「{props.messageForGrandson}」<!--props.xxx 获取外部数据-->
    </div>
  );
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

概括 props 的作用:props 是用来接收父组件传递来的数据,但是这个数据是只读的,不能改写。当然 props 不仅可以接收外部数据,它还可以接收 外部函数,该函数一般是父组件传递过来的,子组件可以在恰当的时机对其进行调用。

2、使用内部数据 state

Vue 的组件内部数据声明在其构造选项 options 的 data 属性中,通过 {{ }} 和 this.xxx 对内部数据进行读写。React 的内部数据则是需要用 state 声明,和使用外部数据 props 一样,React 中类组件和函数组件在使用内部数据时是有所不同的:


类组件 在 constructor() 构造器中通过 this.state = { 键值对 } 的形式声明内部数据。在组件内通过 this.state.xxx  的方式读取组件的内部数据,使用 this.setState({ 键值对 }) 改写组件内部数据。注意:this.setState() 是异步的,不会立即改变 n。


函数组件 用 React.useState(初始值) 返回数组的方式来声明内部数据:const [n, setN] = React.useState(0);。其中数组的第一项为其内部数据 n,第二项 setN() 是改写数据 n 的方法。注意:setN() 是异步的,而且它会声明一个新的 n 覆盖旧的 n。

import React from "react";
import ReactDOM from "react-dom";
function App() {
  return (
    <div className="App">
      爸爸
      <Son />
    </div>
  );
}
class Son extends React.Component {//类组件
  constructor() {                  //需要在constructor()构造器中
    super();
    this.state = {                 //通过 this.state = {} 的形式声明内部数据
      n: 0
    };
  }
  add() {                                   //this.state.n += 1 不行,不像Vue
    this.setState({ n: this.state.n + 1 }); //因为React没有对内部数据做监听,只能调用 this.setState()
                                            //最好使用这种:this.setState( (state) =>{ return state.n + 1});
  }                                         //因为这种方式写两遍可以实现 +2,但是对象的方式却不行
  render() {
    return (
      <div className="Son">
        儿子 n: {this.state.n}    <!--类组件通过 this.state.n 的形式读数据-->
        <button onClick={() => this.add()}>+1</button>
        <Grandson />
      </div>
    );
  }
}
const Grandson = () => {
  const [n, setN] = React.useState(0);
  return (
    <div className="Grandson">
      孙子 n:{n}
      <button onClick={() => setN(n + 1)}>+1</button>
    </div>
  );
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

补充:为什么在 React 的类组件中,不能通过 this.state.xxx +=1;这种形式改写其内部属性?因为 React 不像 Vue 对其内部数据进行了篡改和监听,一旦监听到数据改变就会去刷新视图。React 如果通过 this.state.xxx +=1;这种形式改写数据,视图是不知道的,自然就不会刷新视图了,所以 React 需要通过 this.setState({ 键值对 }) 的形式改写组件内部数据并刷新视图。


建议:在 class 类组件中,如果需要 this.setState() 改写数据,其内部最好传递一个 函数,让函数去改写数据。比如:this.setState({ n: this.state.n + 1 }); 最好能改写成 this.setState( (state) => { return { n: state.n + 1 } }); 的形式。这是因为 this.setState() 是一个异步更新 UI 的过程,如果让你在 this.setState() 下打印 n 的值,代码会先执行打印操作,再去更新 UI,这就导致你拿到的 n 是一个旧的值。如果改成函数的形式,你就可以再函数内部进行打印 n 的操作,如:this.setState( (state) => { const n = state.n+1; return { n: n }); 。


React 的理念是 数据不可变,它会通过 setN() 声明 新数据,而 Vue 的理念是 数据响应式,它提倡改变原数据。

3、复杂 state 的具体使用

上面简单介绍了在类组件和函数组件中 state 的声明及读写方法,但是如果 state 中的数据比较复杂,则情况会有些不一样,比如 state 中有多种类型的数据,包括对象类型,对象中还包括一些简单数据。下面我们来具体分析一下。


类组件 中如果 state 含有多个数据,在改变其中一个数据值的情况下,其它数据会默认自动 延用 之前的值。需要注意的是,上述这种延用机制并不是绝对的,类组件的 setState() 只能自动合并第一层属性(shallow merge): React 只会检查新 state 和旧 state 第一层的区别,并把新 state 缺少的数据从旧 state 里拷贝过来。比如 state 中有一个 person 对象,对象中有 name 和 age 两个属性。这种情况下如果只通过 setState() 改写 name 属性,则 age 属性会被设置为 undefined。


解决第二层合并的问题,可以在 person 对象内部属性前写:...this.state.person 。或者在修改属性的函数内部写上:const user=Object. assign({ }, this. state. user); 或 const user={ ...this.state.user }; 代码。

class Son extends React.Component {
  constructor() {
    super();
    this.state = {   //state中含有 m 和 n 两个数据
      n: 0,
      m: 0
    };
  }
  addN() {
    this.setState({ n: this.state.n + 1 });    //m 不会被覆盖为 undefined,会自动延用之前的值
  }              //或者写成:this.setState({...this.state , n: this.state.n + 1 })
  addM() {
    this.setState({ m: this.state.m + 1 });    //n 不会被覆盖为 undefined,会自动延用之前的值
  }              //或者写成:this.setState({...this.state , m: this.state.m + 1 })
  render() {
    return (
      <div className="Son">
        n: {this.state.n}
        <button onClick={() => this.addN()}>n+1</button>  <!--点击按钮调用 addN() 使 n+1-->
        m: {this.state.m}
        <button onClick={() => this.addM()}>m+1</button>  <!--点击按钮调用 addM() 使 m+1-->
      </div>
    );
  }
}

函数组件 中如果 state 中有多个数据。需要分情况讨论。如果是下面第一种声明变量的形式(m 和 n 分开),那就单个设置就好了,不会有别的问题。如果将数据声明在一个对象中的话({ n:0,m:0 }),则在改变其中一个数据值的情况下,其它数据 不会 默认自动 延用 之前的值,如果非要使用这种方式,可以在 setState() 时将 state先拷贝一份:setState({...state,n:state.n+1})。


函数组件在二级属性上也是不会进行合并的,如果修改一个对象类型的数据的第一个属性值,其余的会被置为 undefined,需要开发者手动使用 ... 操作符进行手动合并。

const Grandson = () => {
  const [n, setN] = React.useState(0);    //不推荐将 m、n 写到一个对象中
  const [m, setM] = React.useState(0);    //那样在设置单个属性时,另一个会被置为 undefined
  return (                                //const [state, setState] = React.useState({n:0,m:0}); 不推荐
    <div className="Grandson">            //如果这种形式,在每次setState时需要:setState({...state,n:state.n+1})
      n:{n}
      <button onClick={() => setN(n + 1)}>n+1</button>
      m:{m}
      <button onClick={() => setM(m + 1)}>m+1</button>
    </div>
  );
};

4、关于两种组件的注意事项

类组件中 this.state.n += 1无效?

解释:其实经过上述这行代码之后的 n 已经改变了,只不过 UI 不会自动更新而已,需要调用 setState() 才会触发 UI 更新(异步更新)。因为 React  没有像 Vue 监听 data 一样监听 state,React 不知道数据什么时候发生改变,所以只能开发人员手动调用 seteState() 刷新视图。


类组件中的 setState() 是异步更新 UI 的

在调用 setState() 之后,state 不会马上改变,立马读取 state 其实是旧的值。更推荐的方式是 setState(函数) 的形式。


类组件 this.setState(this.state)不推荐?

React 的理念是数据不可变,它不推荐开发人员修改旧的 state(不可变数据),然后将修改完的 this.state 传递给 setState() 。而是推荐这种 setState({ n:this.state.n+1 })方式,声明新的 n 数据。


函数组件 setx(新值)

函数组件和类组件在改写内部数据方面比较相似,也要推荐开发人员通过 setx(新值)来更新 UI。


函数组件跟类组件不同的地方

类组件中无论是使用数据还是调用方法,都需要使用 this 关键字,而函数组件没有 this,一律用参数和变量。

目录
相关文章
|
2月前
|
前端开发 JavaScript
学习react基础(3)_setState、state、jsx、使用ref的几种形式
本文探讨了React中this.setState和this.state的区别,以及React的核心概念,包括核心库的使用、JSX语法、类与函数组件的区别、事件处理和ref的使用。
68 3
学习react基础(3)_setState、state、jsx、使用ref的几种形式
|
1月前
|
人工智能 自然语言处理 前端开发
SpringBoot + 通义千问 + 自定义React组件:支持EventStream数据解析的技术实践
【10月更文挑战第7天】在现代Web开发中,集成多种技术栈以实现复杂的功能需求已成为常态。本文将详细介绍如何使用SpringBoot作为后端框架,结合阿里巴巴的通义千问(一个强大的自然语言处理服务),并通过自定义React组件来支持服务器发送事件(SSE, Server-Sent Events)的EventStream数据解析。这一组合不仅能够实现高效的实时通信,还能利用AI技术提升用户体验。
163 2
|
9天前
|
前端开发 UED 开发者
React 数据表格分页实现
本文详细介绍了如何在React中实现数据表格的分页功能,包括基础实现、常见问题及解决方案。通过状态管理和事件处理,我们可以有效地减少页面加载时间,提升用户体验。文章提供了完整的代码示例,帮助开发者解决分页按钮样式、按钮过多和初始加载慢等问题,并给出了相应的优化方案。
84 53
|
2月前
|
前端开发
学习react基础(2)_props、state和style的使用
本文介绍了React中组件间数据传递的方式,包括props和state的使用,以及如何在React组件中使用style样式。
33 0
|
10天前
|
前端开发 搜索推荐 测试技术
React 数据表格排序与过滤
本文介绍了如何在 React 中实现数据表格的排序和过滤功能,从基础概念到实际代码实现,涵盖排序和过滤的基本原理、实现步骤、常见问题及解决方法。通过合理管理状态、优化性能和避免常见错误,帮助开发者提高用户体验和开发效率。
26 4
|
1月前
|
前端开发 JavaScript 调度
React 组件状态(State)
10月更文挑战第8天
23 1
|
2月前
|
前端开发 JavaScript UED
react或者vue更改用户所属组,将页面所有数据进行替换(解决问题思路)____一个按钮使得页面所有接口重新请求
在React或Vue中,若需在更改用户所属组后更新页面所有数据但不刷新整个页面,可以通过改变路由出口的key值来实现。在用户切换组成功后,更新key值,这会触发React或Vue重新渲染路由出口下的所有组件,从而请求新的数据。这种方法避免了使用`window.location.reload()`导致的页面闪烁,提供了更流畅的用户体验。
56 1
react或者vue更改用户所属组,将页面所有数据进行替换(解决问题思路)____一个按钮使得页面所有接口重新请求
|
2月前
|
前端开发
React添加路径别名alias、接受props默认值、并二次封装antd中Modal组件与使用
本文介绍了在React项目中如何添加路径别名alias以简化模块引入路径,设置组件props的默认值,以及如何二次封装Ant Design的Modal组件。文章还提供了具体的代码示例,包括配置Webpack的alias、设置defaultProps以及封装Modal组件的步骤和方法。
68 1
React添加路径别名alias、接受props默认值、并二次封装antd中Modal组件与使用
|
1月前
|
前端开发 JavaScript CDN
React Props
10月更文挑战第8天
12 0
|
2月前
|
前端开发
react中使用Modal.confirm数据不更新的问题解决
文章讨论了在React中使用Ant Design的`Modal.confirm`时更新数据不生效的问题,并提供了解决方案。原因是React状态更新可能是异步的,导致Modal的内容更新后不会立即反映在UI上。解决办法是在状态更新后使用`useEffect`钩子来调用Modal实例的`update`方法,从而更新Modal的内容。
83 0
react中使用Modal.confirm数据不更新的问题解决