react|React 组件与组件实例的三大属性


文章目录

  • 组件
    • 函数式组件
    • class 组件
    • class组件与函数式组件的区别
  • 组件实例的三大属性
    • 三大属性之state
      • setState 是异步更新的
      • setState 什么时候是异步的
    • 三大属性之props
      • props.children 实现类似 Vue 插槽的功能
      • 对props中的属性的值进行类型限制和必要性限制
    • 三大属性之refs
      • createRef创建 ref 容器
      • 回调形式获取DOM节点
【react|React 组件与组件实例的三大属性】
组件 React组件根据定义形式的不同,可以分为函数组件class 组件
在满足需求的情况下,能使用函数式组件就不要使用class组件
注意:组件名称必须以大写字母开头。React 会将以小写字母开头的组件视为原生 DOM 标签。
函数式组件
函数式组件在使用时,会直接调用该函数,并传入props,没有自己的实例,执行完即销毁,无法存储 state
,只能接收props而没有staterefs
function MyComponent(props) { return ( {/*组件无论是使用函数声明还是通过 class 声明,都决不能修改自身的 props*/} 我是函数式组件---{this.props.name} ) }

该函数是一个有效的 React 组件,因为它接收唯一带有数据的 “props”(代表属性)对象,并返回一个 React 元素。这类组件被称为“函数组件”,因为它本质上就是 JavaScript 函数。
class 组件
class组件有自己的状态(state)、refsprops。react每次使用class组件时,都会实例化这个组件,然后重新执行组件实例的render方法(即用多少次,就实例化多少个组件),同时每次有状态(state)更新,就会重新执行render方法
import React from 'react'; export default class LoginComponent extends React.Component { state = {isLogin: true} // 必须实现 render 方法,并返回一个 react 元素 render(){ return ( ) } // 通过赋值语句将箭头函数赋值给组件自身的属性,以解决事件回调中 this 指向的问题 handleClick = () => { // 1、要更改状态不能直接通过this.state.xxx = xxx来访问,而是使用下面的 setState 方法 // 2、当有 state 更新时,react会重新执行组件实例的 render 方法来重新渲染更改 this.setState({ isLogin: !this.state.isLogin }) } }

class组件与函数式组件的区别
class组件 函数式组件
生命周期
组件实例
state和setState
class 组件存在的问题:
  • 大型组件很难拆分和重构,变得难以测试
  • 相同业务逻辑分散到各个方法中,可能会变得混乱
  • 复用逻辑可能变得复杂,如 HOC 、Render Props
所以 react 中更提倡函数式编程,因为函数更灵活,更易拆分,但函数组件太简单,所以出现了hook,hook就是用来增强函数组件功能的。
hook 使用----https://blog.csdn.net/weixin_43842373/article/details/120994912?spm=1001.2014.3001.5502
组件实例的三大属性 组件实例的三个核心属性分别为:statepropsrefs
三大属性之state
  • state是组件对象最重要的属性,值是对象(可以包含多个key-value的组合)
  • class组件被称为"状态机",通过更新组件的state会重新触发组件的render方法来重新渲染页面,state的状态数据不能直接修改或更新,需要借助setState来更新或修改,且更新是一种合并,不是替换。
使用方法如下:当点击按钮后,更新state,react重新触发组件实例的render方法重新渲染,完成登陆切换
import React from 'react'; export default class LoginComponent extends React.Component { state = {isLogin: true} // 必须实现 render 方法,并返回一个 react 元素 render(){ return ( ) } // 通过赋值语句将箭头函数赋值给组件自身的属性,以解决事件回调中 this 指向的问题 handleClick = () => { // 1、要更改状态不能直接通过this.state.xxx = xxx来访问,而是使用下面的 setState 方法 // 2、当有 state 更新时,react会重新执行组件实例的 render 方法来重新渲染更改 this.setState({ isLogin: !this.state.isLogin }) } }

setState 是异步更新的
先来看看setState是什么,看下图:
react|React 组件与组件实例的三大属性
文章图片

setState()会对一个组件的 state 对象安排一次异步更新。当 state 改变了,该组件就会重新渲染。
注意:
调用 setState 其实是异步更新的 —— 不要指望在调用 setState 之后,this.state 会立即映射为新的值。如果你需要基于当前的 state 来计算出新的值,那你应该传递一个函数,而不是一个对象(对象写法其实是函数写法的语法糖),就可以确保每次的调用都是使用最新的 state
incrementCount() { // 注意:这样 *不会* 像预期的那样工作。 this.setState({count: this.state.count + 1}); }handleSomething() { // 假设 `this.state.count` 从 0 开始。 this.incrementCount(); this.incrementCount(); this.incrementCount(); // 当 React 重新渲染该组件时,`this.state.count` 会变为 1,而不是你期望的 3。// 这是因为上面的 `incrementCount()` 函数是从 `this.state.count` 中读取数据的, // 但是 React 不会更新 `this.state.count`,直到该组件被重新渲染。 // 所以最终 `incrementCount()` 每次读取 `this.state.count` 的值都是 0,并将它设为 1。// 问题的修复参见下面的说明。 }

如何解决setState异步更新带来的问题?可以给setState传递一个可以返回对象的函数,而不是一个对象。如下代码:
incrementCount() { // 给 setState 传递一个函数,而不是一个对象 this.setState((preState) => { // 返回的对象会与 preState 合并为新的 state return {count: preState.count + 1} }); }handleSomething() { // 假设 `this.state.count` 从 0 开始。 this.incrementCount(); this.incrementCount(); this.incrementCount(); // 如果你现在在这里读取 `this.state.count`,它还是会为 0。 // 但是,当 React 重新渲染该组件时,它会变为 3。 }

setState 什么时候是异步的
目前,在事件处理函数内部的 setState是异步的。
例如,如果 Parent组件 和 Child组件 在同一个 click 事件中都调用了 setState,如果setState不进行异步更新,那么而就会出现这样的问题:(Child组件被渲染两次,从而影响性能)
  1. Child组件内部的setState完成之后,Child组件会被重新渲染。(Child第一次渲染)
  2. Parent组件内部的setState完成之后,Parent组件会连带着Child组件一起被重新渲染。(Child第二次渲染)
而如果setState使用异步更新,那么在开始重新渲染之前,React 会有意地进行“等待”,直到所有在组件的事件处理函数内调用的 setState() 完成之后。这样就可以确保 Child 不会被重新渲染两次。取而代之的是,React 会将该 state “冲洗” 到浏览器事件结束的时候,再统一地进行更新。这种机制可以在大型应用中得到很好的性能提升。
三大属性之props
props的作用:通过标签属性从组件外向组件内传递变化的数据给props组件内部不建议修改props的数据,数据的更新借助于state
  • 每个组件对象中对会含有props属性
  • 组件标签的所有属性都保存在props中,即相当于自定义属性
  • 组件无论是使用函数声明还是通过 class 声明,都决不能修改自身的 props
看下例:
1、创建一个函数式组件和一个class组件
function MyComponent(props) { return ( {/*组件无论是使用函数声明还是通过 class 声明,都决不能修改自身的 props*/} 我是函数式组件---{this.props.name} ) }class ClassComponent extends React.Component { props = { name: '' } // 自定义属性 name render ( 我是Class组件---{this.props.name} ) }

在使用时如下:

props.children 实现类似 Vue 插槽的功能
参考官网—https://zh-hans.reactjs.org/docs/react-api.html#reactchildren
map、forEach、only、count、toArray
react是单向数据流,props是我们从上个组件传值传下来的集合,每一个props都应该对应的是从上个组件传过来的值。但是这里有一个例外,那就是this.props.children。如下示例:
import React from 'react' import ReactDom from 'react-dom' class PropsChildren extends React.Component { constructor(props) { super(props) } render() { console.log(this.props.children) // 接收标签体内容 return ( {this.props.children} ) } }ReactDOM.render( // 通过闭合标签写入标签体内容,写入的标签体内容会自动传给 props.children标签体内容---props.children 这是一个时钟组件 document.getElementById('root') );

效果如下:
react|React 组件与组件实例的三大属性
文章图片

可以看到:this.props.children 的值有三种可能:
  • 如果当前组件没有子节点,它就是 undefined ;
  • 如果有一个子节点,数据类型是 Object 或者 String,这取决于写入的标签体内容是一个组件还是普通字符串;
  • 如果有多个子节点,数据类型就是 Array
所以,处理 this.props.children 的时候要小心。因为不能保证 this.props.children 是一个数组,所以如果使用this.props.children.map遍历它的时候可能会抛出错误。React 提供一个工具方法 React.Children 来处理 this.props.children 。可以用 React.Children.map 来遍历子节点,而不用担心 this.props.children 的数据类型是 undefined 还是 Object Array。示例如下
class MyList extends React.Component { render() { return (
    { React.Children.map(this.props.children, function(child){ console.log(child)// MyList里面有节点,对这三个节点进行循环 return
  • child
  • ; }) }
); } } ReactDOM.render( >飞剑侠 >武尊神 >逆天魔 ,document.getElementById('example'));

对props中的属性的值进行类型限制和必要性限制
在使用props传递变化的数据时,有时组件内部对于数据有一定的要求,就需要对props的属性值进行类型限制和必要性限制
常用方法是使用prop-types库进行限制(需引入prop-types库):
import PropTypes from 'prop-types'; import React from 'react' export default class ClassComponent extends React.Component { props = { // 自定义属性 name, age, speak name: '', age: 0, speak: null } // 对props进行限制 static propTypes = { name:PropTypes.string.isRequired, //限制name必传,且为字符串 age:PropTypes.number,//限制age为数值 speak:PropTypes.func,//限制speak为函数 } render ( 我是Class组件---{this.props.name} ) }

三大属性之refs refs可以获取DOM节点,以此来操作DOM。在组件内使用,组件内的便签可以定义ref属性来表示自己
注意:字符串形式的 ref 使用方式已不被 React 官方推荐,尽量减少使用
createRef创建 ref 容器
class Demo extends React.Component{ /* React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点,该容器是“专人专用”的 */ myRef = React.createRef() // 1、创建ref容器 myRef2 = React.createRef() //展示左侧输入框的数据 showData = https://www.it610.com/article/()=>{ alert(this.myRef.current.value); } //展示右侧输入框的数据 showData2 = ()=>{ alert(this.myRef2.current.value); } render(){ return(
{/*2、在标签上使用 ref 之后,myRef 容器中便存放了这个节点*/}      
) } }

回调形式获取DOM节点
class Demo extends React.Component{ //展示左侧输入框的数据 showData = https://www.it610.com/article/()=>{ const {input1} = this // 2、从 this.input1 中获取DOM节点 alert(input1.value) } //展示右侧输入框的数据 showData2 = ()=>{ const {input2} = this alert(input2.value) } render(){ return(
{/*1、通过回调函数将 DOM 节点赋值给 input1 属性*/} this.input1 = c } type="text" placeholder="点击按钮提示数据"/>    this.input2 = c } type="text" placeholder="失去焦点提示数据"/> 
) } }

    推荐阅读