react 函数式组件和类组件引发对于 props 和 this.props 可变不可变的探讨。

1,759 阅读2分钟

前言

当我在网上搜这个问题 函数式组件和类组件的区别 的时候,看到的都是关于大神这篇文章的复制,大家没看过的可以先看看,最主要的一个问题就是讨论就是类组件的 this 是可变的,函数组件的 props 是不可变的,但是却没看到一篇文章说为什么,都是文章中说的React 对于 this 是可变的,对于 props 是不可变的。我想说***。

我来说说原因

class 组件和类组件的渲染机制

class 是通过 new class() 创建的实例,调用 render 执行渲染,函数是直接执行渲染。

// 类组件
class App extends React.Component {
  render() {
      return <jsx />
  }
}
new App().render();

// 函数组件
function App() {
    return <jsx />
}
App();

class 组件和类组件的更新机制

  • 组件的 state 变化,class 是通过 this.render() 更新的组件,在执行相关的生命周期。
  • 函数是重新调用方法更新(这也是为什么函数的方法会执行多次的原因)。

props 并不是不可变

准确来说是 props 在当前作用域不可变,因为形成了闭包。而 props 本身就会变. props依赖于父组件,父组件传给子组件参数改变时候,props就已经变了。

// props 可变,点击  Change Value,你就已经看到 value 的值已经变了
function Component({ value, setValue }) {
  return (
    <div>
      <div>{value}</div>
      <div
        onClick={() => {
          setValue("Change Value");
        }}
      >
        Change Value
      </div>
    </div>
  );
}

function App() {
  const [value, setValue] = useState("value");
  return <Component value={value} setValue={setValue} />;
}

// props 不可变
function Component({ value, setValue }) {
  const input = () => {
    setTimeout(() => {
      console.log("value", value);
    }, 2000);
  };
  return (
    <div>
      <div>{value}</div>
      <div
        onClick={() => {
          setValue("Change Value");
          input();
        }}
      >
        Change Value
      </div>
    </div>
  );
}

function App() {
  const [value, setValue] = useState("value");
  return <Component value={value} setValue={setValue} />;
}

export default App;

原因

  • 函数式组件会形成闭包,劫持 props,所以 props 不可变,

    // 如上: props 并不是不可变
    
  • class 不会形成闭包,this 拿到的一直是最新的值。当 state 改变时,会调用 render 方法更新组件,doSomething 方法并没有形成闭包

    class Child extends React.Component {
      render() {
        return (
          <div onClick={() => this.props.doSomething()}>{this.props.value}</div>
        );
      }
    }
    
    class App extends React.Component {
      constructor(props) {
        super(props);
        this.state = { value: "123" };
      }
    
      doSomething = () => {
        this.setState({ value: "value" });
        setTimeout(() => {
          console.log("value", this.state.value);
        }, 2000);
      };
      render() {
        return <Child doSomething={this.doSomething} value={this.state.value} />;
      }
    }
    
  • class 如何形成闭包

    class Child extends React.Component {
      render() {
        const props = this.props;
        const input = () => {
          console.log("pre props", props);
        };
        console.log("cur props", this.props);
        return (
          <div
            onClick={() => {
              this.props.doSomething();
              input();
            }}
          >
            Click
          </div>
        );
      }
    }
    
    class App extends React.Component {
      constructor(props) {
        super(props);
        this.state = { value: "value" };
      }
    
      doSomething = () => {
        this.setState({ value: "cur value" });
        console.log("async cur value", this.state.value);
        setTimeout(() => {
          console.log("cur value", this.state.value);
        }, 2000);
      };
    
      render() {
        return <Child doSomething={this.doSomething} value={this.state.value} />;
      }
    }
    

总结

上面运行的 Demo

闭包,自己看去吧