React-state状态和props进阶

遗留问题

事件处理方法的参数传递

类组件: 


import React, { Component } from 'react'

export default class App extends Component {
  say(e,name){
    console.log(e);
    console.log(name + '你好');
  }
  say1(name, e){
    console.log(e);
    console.log(name + '你好');
  }
  // 想要事件对象  事件对象会传给事件对象的事件处理方法  
  // 结合事件传参 常用两种方案  第一种是利用箭头函数中转调用事件处理函数传参  第二种利用bind绑定this指向传参
  render() {
    return (
      
App {/** 事件处理方法 是 箭头函数方法 形参上具有事件处理的形参 */} {/* 事件处理默认传递事件参数 传递name参数时占位形参 */}
) } }

函数组件:


// 函数组件事件的使用
import React from 'react'

// rfc 类似于vue中的组合式API

export default function App() {
  const text = '我来了'
  const sayHello = (name ,e)=> {
    console.log(text); // 这里不用调用this 可以直接拿到 text 
    console.log(name);
    console.log(e);
  }
  return (
    
) }

state状态

如果将state与vue中的某个点做类比的话,则其相当于vue组件中的data,作用就是用于存储当前组件中需要用到的数据。

状态就是组件描述某种显示情况的数据,由组件自己设置和更改,也就是说由组件自己维护,使用状态的目的就是为了在不同的状态下使组件的显示不同。

state状态只在class类组件才有,函数组件没有此功能!

1.基本使用

  • 状态(state)即数据,是组件内部的私有数据,只能在组件内部使用

  • state的值是对象,表示一个组件中可以有多个数据

  • 通过this.state.xxx来获取状态(对应vue的this.xxxx)

  • state数据值只能通过this.setState()来修改(与vue不同)

  • Do not mutate state directly. Use setState()

  • state可以定义在类的构造方法中也可以写在类的成员属性中

第一种设置方式:


import React, { Component } from "react";

class App extends Component {
    // 构造函数初始state
    constructor(props) {
        super(props);
        this.state = {
            count: 0,
        };
    }
    render() {
        return 
{this.state.count}
; } } export default App;

第二种设置方式(推荐):


import React, { Component } from "react";

class App extends Component {
    // 常规初始化
    state = {
        count: 0,
    };
    render() {
        return 
{this.state.count}
; } } export default App;

案例:


// 组件状态
// 组件内部需要变动的值
import React, { Component } from 'react'

export default class App extends Component {
  // 初始化组件状态
  state = {
    num: 100
  }
  add = () => {
    // sst 快捷生成
    // 修改组件状态
    // setState 修改state状态 更新页面
    // eslint-disable-next-line react/no-direct-mutation-state
    this.setState({ num: ++this.state.num })  //方法一 正规写法
    // this.state.num += 1
    // this.setState({})  //方法二
    // 强制更新
    // this.state.num += 1
    // this.forceUpdate()  //方法三 不推荐直接使用
  }
  render() {
    return (
      
) } }

切记:不要直接通过this.state.xxx = xxxx形式去更改state的值。否则会包警告,警告如下:Do not mutate state directly. Use setState()。

2.修改状态

在vue中,data属性是利用Object.defineProperty处理过的,更改data的数据的时候会触发数据的gettersetter,但是React中没有做这样的处理,如果直接更改的话,react是无法得知的,所以,需要使用特殊的更改状态的方法setState

setState接受2个参数,第一个参数负责对state自身进行修改(必须的),我们称之为updater;第二个参数是一个回调函数(可选),因为setState方法是异步的,如果想在更新好状态后做进一步处理,此时就可以用到第二个参数了。

语法:this.setState(updater[,callback])

updater参数传递的时候支持两种形式:对象形式==函数形式==

对象形式:


this.setState({
    count: this.state.count + 1
})

函数形式:


this.setState(state => {
    return {
        count: state.count + 1
    }
})

上述两种参数形式的updater建议使用函数形式。因为对象形式在批量使用的时候会存在问题,因此建议使用函数形式。

案例:

// 组件状态
// 组件内部需要变动的值
import React, { Component } from "react";

export default class App extends Component {
  // 初始化组件状态
  state = {
    num: 100,
  };
  add = () => {

    // 对象写法
    // this.setState({ num: this.state.num + 1 });
    
    // 函数写法  // 在一个操作中同时操作多次方法的时候 使用 函数写法 
    this.setState(state => {
      return{
        num : this.setState.num +1
      }
    })
    console.log(this.state.num);
  };
  render() {
    return (
      
); } }

3.总结:

Q1:state与props有什么区别?

    a. state是组件自身数据的管理对象而props是别的组件传递来的数据的管理对象;

    b. state是可以修改的,而props是不能修改的;

    c. state只能在类组件中使用,而props既可以在类组件中使用也可以在函数组件中使用; 

Q2:this.setState()是同步的还是异步的?

    该方法既有同步实现也有异步实现。如果代码进入了react代码流程,则是异步的(居多),否则(原生js,典型的有定时器)是同步的。

案例:

// 组件状态
// 组件内部需要变动的值
import React, { Component } from "react";

export default class App extends Component {
  // 初始化组件状态
  state = {
    num: 100,
  };
  add = () => {
    // 异步操作原因 : js渲染为单线程  如果中间有任务卡顿 页面会一直加载  所以 为了防止页面渲染阻塞 等它完成之后再进行页面渲染
    // 对象写法
    // this.setState({},回调函数返回异步处理后的结果)
    this.setState({ num: this.state.num + 1 }, ()=>{
      console.log(this.state.num);
    });  //异步操作   为了防止页面渲染阻塞 

    console.log(this.state.num);
  };
  render() {
    return (
      
); } }

props进阶

1.children属性

在vue中对应的是插槽Slot(匿名插槽)。

children属性表示组件标签的子节点,当组件标签有子节点时,接受传值的子组件中的props里就会有该属性,与普通的props一样,其值可以使任意类型。

例如,在父组件中有如下代码:

import React, { Component } from "react";
import Cmp from './Components/Cmp'

class App extends Component {
    state = {
        content: "蚂蚁集团A股发行价确定",
    };
    render() {
        return {this.state.content};
    }
}

export default App;

上述代码中的{this.state.content}即为子组件标签中的子节点,那么在子组件的props属性里就存在一个children属性可以被我们使用。如下:


import React, { Component } from "react";

class Cmp extends Component {
    render() {
        return 
{this.props.children}
; } } export default Cmp;

需要注意,如果子组件标签里只存在一个子节点,则children属性值为一个字符串;如果子组件标签里存在多个子节点,那么children属性的值为一个索引数组。

简而言之,上述写法形式有点像Vue里的插槽,但是不是,children这一小结所讲的内容简单来说就是父传子的另外一种写法而已:原先父传子是将值写在了组件标签的属性中,只不过现在写在了组件标签里而已。

案例:

import React, { Component } from 'react'

/**
 * children属性
 *  插入组件 或者 html 标签  
 */
export default class App extends Component {
  render() {
    return (
      
App

父标题 x

父标题 2

) } } class Chlid extends Component{ render(){ console.log(this.props); return(
子组件 {this.props.data}
{this.props.children}
{this.props.children[0]}
) } }

2.prpos-type

关于JavaScript的class中的静态成员与常规成员


class App {
  static uname = 'zhangsan'
  username = "lisi"
}
console.log((new App).username);
console.log(App.uname);
// 常规的属性是在对象里的,如果要用得先实例化
// 静态属性是类里面的,使用的时候不要实例化
// 静态成员要优先于常规的成员


new Vue({
  el: "#app",
  data() {
      return {}
  },
  // props: ["one","two"],
  props: {
      one: {
          type: String,
          default: "1",
      },
      two: {
          type: Number,
          default: 1,
      },
  },
})

React是为了构建大型应用程序而生,在一个大型应用开发过程中会进行多人协作,往往可能根本不知道别人使用你写的组件的时候会传入什么样的参数,这样就有可能会造成应用程序运行不了但是又不报错的情况。所以必须要对于props设传入的数据类型进行校验。

为了解决这个问题,React提供了一种机制,让写组件的人可以给组件的props设定参数检查,需要安装和使用prop-types:(新版之后已经不需要安装)


# 现在的react的版本已经直接将其作为依赖,因此可以免于安装
npm i -S prop-types

在使用时,无论是函数组件还是类组件,都需要对prop-types进行导入:


import PropTypes from "prop-types"

随后依据使用的组件类型选择对应的应用方式,如果是函数组件则按照下面的方式应用:


function App(props){
    // 函数组件声明过程
    return "";
}
// 为App方法组件挂上验证规则
App.propTypes = {
    // 待验证的属性名:PropTypes.类型规则[.isRequired]
    propname:PropTypes.string,
    // ... 
}

如果是类组件则按照下面的方式应用:


class App extends Component{
    // 类内部完成检查
    static propTypes = {
       // 待验证的属性名:PropTypes.类型规则[.isRequired]
       prop-name:PropTypes.string
    }
  // 渲染
  render() {
        return "";
    }
}

本质上来上面类组件的写法与方法组件的写法是一样的,只不过考虑到类组件的代码完整性,我们把规则的验证做成了类的静态成员属性的方式,如果还原成最初的代码则如下:


class App extends Component {
    render() {
         return 
{this.props.flag}--{this.props.num}
; } } App.propTypes = { flag: PropTypes.string, num: PropTypes.number.isRequired, };

需要注意,isRequired规则必须放在最后且不能独立于其他规则存在。更多的验证规则,可以参考React官网。

3.默认值

如果props有属性没有传来数据,为了不让程序异常,我们可以依据业务情况给对应的属性设置默认值。

依据组件类型的不同选择对应的操作方式,如果是函数组件则采用如下方式:

function App(props){
    // 函数组件声明过程
    return "";
}
// 为App方法组件挂上默认值
App.defaultProps = {
    // 属性名:默认值
    title: "标题",
    // ...
}

如果使用的是类组件则使用如下方式:


class App extends Component {
    // 添加静态成员属性`defaultProps`设置props的默认值
    static defaultProps = {
        // 属性名:默认值
        title: "标题"
        // ...
    }
}

上述知识点案例:

子组件:


// rcc props 类型限定
import React, { Component } from 'react'
import PropTypes from "prop-types"
export default class Cards extends Component {
  // 静态属性设置  组件props类型限制
  static propTypes = {
    // data:PropTypes.object// 限定传输过来的值的类型
    data:PropTypes.shape({   // 传输过来的值的内容的类型限定
      name: PropTypes.string,
      job: PropTypes.string,
      sex:PropTypes.bool
    })
    
  }
  render() {
    const {name,job,sex} = this.props.data
    return (
      
姓名: {name}
职位: {job}
性别: {sex? '男' : '女'}
{this.props.Fname}
) } } Cards.defaultProps = { // 如果没传输值 则默认这个值 Fname: '未知', }

父组件:

import React, { Component } from "react";
import Cards from "./compontens/Cards";

export default class App extends Component {
  data = {
    name: 'peilong',
    job: 'web开发',
    sex:false
  }
  render() {
    return (
      
App {/* 组件的属性值不为字符串时, 都需要通过{} 解析 */} {/** 如果不穿则为默认值 */}
); } }

你可能感兴趣的:(react.js,javascript,前端)