(12)React 进阶——② state 、props 与 render 函数的关系 | React 基础理论实操

6,530 阅读4分钟
转载请注明出处,未经同意,不可修改文章内容。

🔥🔥🔥“前端一万小时”两大明星专栏——“从零基础到轻松就业”、“前端面试刷题”,已于本月大改版,合二为一,干货满满,欢迎点击公众号菜单栏各模块了解。

涉及面试题:
 1. React 的状态是什么?
 2. React 中的 props 是什么?
 3. 状态和属性有什么区别?
 4. 为什么不能直接更新状态?
 5. 回调函数作为 setState() 参数的目的是什么?
 6. 如果在构造函数中使用 setState() 会发生什么?
 7. 构造函数使用带 props 参数的目的是什么?
 8. 什么是调解?
 9. 如何使用动态属性名设置 state?
10.React 中如何校验 props 属性?
11. 如果在初始状态中使用 props 属性会发生什么?
12. 为什么在 DOM 元素上展开 props 需要小心?
13. 为什么需要将函数传递给 setState() 方法?
14. 如何在 attribute 引号中访问 props 属性?
15. setState() 和 replaceState() 方法之间有什么区别?
16. 更新状态中的对象有哪些可能的方法?
17. 为什么函数比对象更适合于 setState()?
18. 如何使用 setState 防止不必要的更新?
19. 什么时候组件的 props 属性默认为 true?

编号:[react_12]

1 state 与 render 函数的关系

通过之前文章的学习,我们可以很明显地感受到,React 是一门由“数据”驱动的框架——当数据发生变化,页面就会自动地跟着发生变化。

❓这背后的原理是怎样的呢?
答:(先给出结论,再详细讲解)当组件的 state 或者 props 发生改变的时候, render 函数就会重新执行。而页面又是 render 函数渲染出来的,故数据发生变化,页面就会自动地跟着发生变化!

紧接上一篇的代码,我们打开 TodoList.js 文件。

1️⃣先演示 state 发生变化,页面跟着变化的情形:

import React, { Component, Fragment } from "react"; 

import TodoItem from "./TodoItem"; 

import "./style.css"; 

class TodoList extends Component {
  constructor(props) {
    super(props);
    this.state = { /*
    							 1️⃣-①:一旦 state 发生改变(我们可以在页面的 input 框输入任意内容,
      						 来使 state 发生改变),
                    */
      inputValue: "", 
      list: []
    };
    
    this.handleInputChange = this.handleInputChange.bind(this);
    this.handleBtnClick = this.handleBtnClick.bind(this);
    this.handleItemDelete = this.handleItemDelete.bind(this);
    
  }

  render() { // 1️⃣-②:render 函数就会重新执行(即,重新用新的数据渲染页面)。
    console.log("render") /*
    											1️⃣-③:为了演示 render 函数确实执行了,
    											我们让控制台给我们实时打印一些信息。
                           */
    
    return(
      <Fragment>
        <div>
          <label htmlFor="insertArea">请输入要进行的事项:</label>    
        
          <input 
            id="insertArea"
      
            className="input"
            value={this.state.inputValue}
            onChange={this.handleInputChange}
          />

          <button onClick={this.handleBtnClick}>
            提交
          </button>
        </div>

        <ul>
          {this.getTodoItem()}
        </ul>

      </Fragment>
    )
  }

  getTodoItem() {
    return this.state.list.map((item, index) => { 
      return( 
        <TodoItem 
        	key={index}
        
        	content={item}
        	index={index} 
        	itemDelete={this.handleItemDelete}
        />  
      )  
    })
  }
  
  handleInputChange(e) {
    const value = e.target.value
    
    this.setState(() => ({
      inputValue: value
    }))
  }

  handleBtnClick() {
    this.setState((prevState) => ({
      list: [...prevState.list, prevState.inputValue],  
      inputValue: ""        
    }))
  }

  handleItemDelete(index) { 
    this.setState((prevState) => {
      const list = [...prevState.list]
      list.splice(index, 1)
        
      return {list}
 
    })
  }
}

export default TodoList;

看下页面效果(我们在 input 框的每次输入都改变了 state,所以 render 函数也相应地在执行):

react_12-01.gif

2 props 与 render 函数的关系

2️⃣打开 TodoItem.js 文件,我们继续演示 props 发生变化,页面跟着变化的情形:

import React, { Component } from "react";

import PropTypes from "prop-types";

class TodoItem extends Component {
  constructor(props) {
    super(props);
    
    this.handleClick = this.handleClick.bind(this);
  } 
  
  render() {
    console.log("TodoItem render") /*
    													 2️⃣-③:在页面 input 框输入内容并点击“提交”后,
      												 子组件的 render 函数都会相应执行。
                               ❗️❗️❗️其原因有二:
                               第一,子组件的 content 是从父组件接收的,在父组件 TodoList 里,
                               content 的值是“列表循环出的每一项”。故,当我们输入内容并提交后,
                               “列表循环出的每一项”会随之变化,继而子组件接收到的属性也会变化,
                               子组件的 render 函数相应地会根据新的值进行重新渲染;
                                      
                               第二,当父组件的 render 函数被运行时,它的子组件的 render 函数
                               都将被运行一次!
                               因为,子组件 TodoItem 是被父组件 TodoList 的 render 渲染出来的,
                               那么,父组件的 render 在被重新执行时,子组件的 render 也会被执行。
                                    */
    
    const {content, test} = this.props /*
    																	 2️⃣-①:this.props 是父组件
                                       通过“属性”传递过来的内容;
                                        */
    
    return(
      <div onClick={this.handleClick}> 
      {test}{content} {/* 2️⃣-②:我们将内容项 content 展示在页面; */}
      </div>
    )
  }
  
  handleClick() {  
    const {itemDelete, index} = this.props  
    
    itemDelete(index)
  }
}

TodoItem.propTypes = {
  
  test: PropTypes.string.isRequired, 
  
  content: PropTypes.string,  
  itemDelete: PropTypes.func,
  index: PropTypes.number
}

TodoItem.defaultProps = {
  
  test: "hello, " 
}

export default TodoItem;

看看页面效果,结合本篇所讲和之前实现代码时的逻辑,尝试对控制台打印出的每一行内容作分析(思路很清晰了对不对~):

react_12-02.gif

有了本篇的基础,我们就可以比较轻松地去学习“虚拟 DOM”的知识了。待“虚拟 DOM”的三篇文章学完,再回来看本篇文章时,你将会对 React 的“数据驱动”思想有更深的理解!

祝好,qdywxs ♥ you!