02 - react State异步问题、props数据校验、生命周期

笔记批注:易错 参考文献 重点 面试题

01 props

01.01 父子组件传值(插槽使用,类型控制)

  1. 插槽,props数据传值;
  2. props 控制数据类型;
    2.1 法一:配置静态属性;
    2.2 法二:配置类属性;
  • Button.js
    import React, {Component} from "react";
    
    class Button extends Component {
     render() {
        return {
           console.log(this.props)
          <button>{this.props.children}</buttton> // 打印出来是“增加”;
        }
      
      }
    }
    export default Button
    
  • Child.jsx
    import React, {Component} from "react";
    import Button from "./Button"
    import types from "prop-types"//2.1
    class Child extends Component {
      render(){
        /**
        * 2. 配置校验规则:
        * 变量必须静态属性 -- static -- 不能在实例上调用
        * 属性名字与state一样固定不能够修改;
        */
        static propTypes = {
          // count: String 不支持;
          count: types.number.isRequired //2.2 必须先类型再是否必传;类型错误,必传效果都会检验报错;
          myAddEvt: types.func
        }
        addEvt(step){
          this.props.myAddEvt(5);
        }
        return {
          <div>
            Child.Component...
            <p>
              父组件的count: {this.props.count}
              <Button>{} 增加</Button> // 1. 没有插槽,放在Button(子组件)内部的文字,使用Children传递给子组件;
            </p>
          </div>
        }
      }
    }
    // 2.3 通过类属性配置校验规则;
    Child.propTypes = {
      // count: types.number.isRequired //2.2 必须先类型再是否必传;类型错误,必传效果都会检验报错;
      myAddEvt: types.func
    }
    // 2.3.1 设置默认值
    Child.defaultProps = {
      count: 10000
    }
    export default Child
    
  • App.jsx
    import React, {Component} from "react";
    import Child from "./Child"
    
    class App from extends Component {
      state = {
        count: 10
      }
      addEvt = frunction(step){
        this.setState({
           count: this.state.count + step;
        })
      
      }
      render(){
        return(
          <div>
            App Page...
            <hr>
              // 1. 需要的字段都传递了;
            <Child count={this.state.count} myAddEvt={this.addEvt.bind(this)}>
            <hr>
              // 2. 需要的字段没有完全传递;
              // 2.1 count传递的是字符串 ==》 报错;
              // 2.2 myAddEvt传递的是数字不是函数 ==》 报错;
            <Child count="12" myAddEvt={ 23 }/>
          </div>
        )
      }
    
    }
    
  • 插槽:在react里面没有插槽的说法;react中组件中传递数据,子组件中使用children来接收 //this.props.children;
  • props控制数据类型:
    • 数据类型校验: npm.js库中 prop-types
    • 安装命令:yarn add props-types;

01.02 状态提升解读

  1. A的数据B用;A传给App.jsx, App.jsx传给B;
  2. 变量提升:变量其他组件使用,就不要定义再这个组件内部;
    • 变量this指向;
  3. 受控组件:组件内部的所有属性都不定义在自己内部;
  4. 受控表单:组件内部,没有自己的state数据,也没有自己修改数据的方法;
  • PageA.jsx
      import React. {Component} from "react"
      export default PageA from Component {
         // state = {
         //  count: 10
        // }
        render() {
          return (
            <div>
              PageA page... {this.state.count}
              <input type="text" value={this.props.count} onChange={(evt) => {
                console.log(evt.target.value) // evt 这里拿到的是字符串,因此在iptEvt setState 中必须转为数据类型;
                this.props.inputEvt(evt.target.value) //调用this上
              }}> // 4.0 受控表单
            </div>
          )
        } 
      }
    
  • PageB.jsx
      import React. {Component} from "react"
      export default PageB from Component {
        state = {
        
        }
        render() {
          return (
            <div>
              PageB page... {this.props.count} <button onClick={
                  () => {
                      this.props.munisEvt(2);
                  }
              }>减少</button> // 1.3 合成事件中可以直接编写方法;
            </div>
          )
        } 
      }
    
  • App.jsx
      import React , {Component} from "react"
      import PageA from "./PageA"
      import PageB from "./PageB"
       class App extends Component {
        render() {
        state = {
          count: 1000 // 1.1
        }
        minusEvt(step){
          this.setState({
            count: this.state.count  - step;
          })
        }
        iptEvt(val) {
          this.setState({
             count: val *1; // 转换方式1;
             count: +val // 隐式转换方法; 类似于+ new Date()
          })
        }
          return {
            <div>
              App.page...
              <hr>
              <PageA count={this.state.count} iptEvt={this.iptEvt.bind(this)}/> //将iptEvt 绑定上去; 
              <PageB count={this.state.count} munisEvt={this.munusEvt.bind(this)}/>// 1.2 变量提升;
    
            </div>
          }
        }  
      }
    

01.03 基础生命周期(主要面试,实际开发使用少)

  1. 生命周期
    • 作用: 在一个组件从创建到销毁的特殊环节暴露出来的钩子函数,允许我们往钩子函数中添加业务。
    • 应用:组件初始化的时候,要获取数据,那么可以在创建生命周期来调用获取数据的接口。
    • 对比vue:vue18个生命周期,react按照js执行顺序定义生命周期。
    • 独有:只有类组件有生命周期,函数组件没有生命周期。
    • 阶段:react生命周期只有三个阶段,业务完成后执行下一次任务。挂载,更新,卸载
App.jsx
  import React,{Component} from "react"
  export default class App extends Component{
    render(){

      constructor(){
        // 1.1 构造器就是第一个生命周期;
          this.state = {}
      }
      static getDerivedStateFromProps(_newProps,_state) {//静态方法没有this; 没有new运算;
        console.log(arguments)
         // 1.2.3 react组件更新有可能是state数据变化,props变化,生命周期等价于挂载和更新生命周期;
        // 1.2 第二个生命周期:生命周期会将数据对象合并到state对象里,log的this打印出来为state.count
        // 1.2.1 应用场景:props数据不可以更新,但同时不会影响父组件。
        // 1.2.2 一旦使用该生命周期必须定义state数据;
        return {
          count: 10//1.2.2;
          count: _newProps.count// 1.2.3
        }
        // 1.2.3 react生命周期getDerivedStateFromProps需要判断具体在哪个状态,否则不生效;
          return _state.count ? null : {
          count: _newProps.count
        }
      }
      console.log(this,arguments) 
      // 1.3 第五个生命周期;类似于vue的mounted和onMounted生命周期;
      componentDidMount(){
        console.log("五,前面三四不常用");
        // 1.3.1 react 挂载生命周期阶段,只有componentDisMount可以执行setState方法;
      }
     // 1.4 第六个生命周期;更新完成生命周期;
      conmponentDisUpdate(_newProps, _oldState){ // 1.4.1 state数据是更新前的;现在的state没有;可以用this.state.count替代;
        console.log("更新完成生命周期");
        console.log(arguments)
      }
      return (
        <div>
          App Page... { this.props.count },{ this.state.count}//1.2.2此时两个值分别为1000,10;
          App Page... { this.props.count },{ this.state.count}//1.2.3此时两个值分别为1000,1000;
          // 1.2.4此时添加按钮,操作count;this.props.count不会修改,this.state.count就是获取到的衍生数据会被修改;
          <button onClick={this.state.count ++ }>增加</button> //可能有问题
          // 1.3.1 render方法不能执行setState方法,因为setState会调用render,形成递归;setState调用render,render调用state;
         // button上面调用方法;
          <button onClick={() => { this.setstate({
                              count: this.state.count +1
                                })
                             }}>
        </div>
        )
    }
  }
index.js
  import React,{Component} from "react";
  import App from "./App.jsx";
  const root = React.CreateRoot(document.getElementById("root"));
  root.render(
    <App count = {1000} />
  )

面试题: Object.assign 是浅拷贝, 数据对象只有一层的时候是深拷贝;

  1. 卸载生命周期;
    • 唯一可以使用的will的生命周期,react官网可查(其他的基本不使用UNSave)
    • componentWillUnmount()
Child.jsx
  import React,{Component} from "react";
  export default class Child extends Component{
    state = {
      timer: 0;
    }
    componentDidMount(){
      let timer = setInterVal(function(){
        console.log('1')
      },1000)
      this.setState({timer }) // 单项数据流,设置比较特殊不同于vue;
    };    
    componentWillUnmouont(){
      clearInterval(this.state.timer) // 离开页面的时候销毁定时器;
    };
    render(){
      return(
        <div>
          Child.jsx组件;
          <hr/>
          <p>我的组件卸载;</p>
        </div>
      )
    }
    
  }
App.jsx
  //1. 父组件;
  import React , {Component} from "react"
  import Child from "./Child.jsx"
  export default class App extends Component {
    constructor(){
      show: 
    }
    render () {
   // 1.1 卸载组件(条件渲染);
      return(
        <div>
          App Page..
          <button class="toggle" onClick={
              () => {
                this.setState({show: !this.state.show}) // 1.3 变量的修改方法;setState的使用;
              }
            }>切换</button> 
          {
            this.state.show && <Child> // 1.2 并且运算符,前面为真继续向后运算,否则直接停止;
          }
      </div>
      )
    }
  }
  1. 渲染优化生命周期;
App.jsx
  import React, {Component} from "react";
  export default class App extends Component{
      state = {
        list: Array.from(Array(6),(_,i) =>({
            id: "id"-i,
            name: "化为手机",
            price: 4000,
            count: 1
        }))
      };
      addEvt(id,step){
          this.setState({
          list: this.state.list.map(it => {
            if(it.id === id) it.count += step
            return it;
          })
        })
      };
    render(){
      return (
          <div>
              <table className="table">
                <thead>
                    <tr>
                      <th>商品名字</th>
                      <th>商品价格</th>
                      <th>商品数量</th>
                    </tr>
                </thead>
                <tbody>
                  {this.state.list.map((item) => {
                    return (
                         <tr key={it.id}> // 不要使用下标作为key;
                            <td>{it.name}</td>
                            <td>{it.price}</td>
                            <td>
                              <button onClick={this.addEvt.bind(this,it.id,2)}>+</button>
                              {it.count}
                               <button>-</button>
                            </td>
                            <td></td>
                          </tr>
  
                        )
                    })}
                </tbody>
              </table>
          </div>
      )
    }
  }
item.jsx
  import React,{Component} from "react";

重点: key不要使用index,原理:数组若存在增加删除等排序操作,key的顺序就会乱,失去了作用;

  1. 直接将render中的list组件遍历在App.jsx 中,每次修改一个数量,整个组件都会重新渲染,将其单独抽出新的组件Item;
posted @ 2025-07-21 14:14  Saturday_to_Monday  阅读(7)  评论(0)    收藏  举报