reactjspure-function

Why props in React are read only?


The React documentation says: React is pretty flexible but it has a single strict rule: all React components must act like pure functions with respect to their props.

Why is that?

I guess that if you change directly the value of the props, the component does not re-render, that's why we must use setState. But I still don't understand the reason behind this. Why components must be like pure functions with respect to their props?


Solution

  • The important concept of React component: a component should only manage its own state, but it should not manage its own props.

    In fact, props of a component is concretely "the state of the another component (parent component)". So props must be managed by their component owner. That's why all React components must act like pure functions with respect to their props (not to mutate directly their props).

    I will show you a simple example:

    class ParentComponent extends Component {
      constructor(props) {
        super(props);
        this.state = {
          p1: {a:1, b:2},
        }
    
       render() {
          return <ChildComponent p1={this.state.p1} />     
       }
    }
    

    In the ChildComponent, if you want to mutate the "passed prop p1" (p1 is an object with his own ref) (ex. in the ChildComponent, you write: p1.a=3), so evidently, "the p1 - property of the state of ParentComponent" is also mutated. But the ParentComponent couldn't re-render in this case because you didn't trigger the action setState() in ParentComponent. So it will generate many uncontrolled bugs for a React App unstable.

    I hope right now that you can understand why React says:

    The strict rule: all React components must act like pure functions with respect to their props (not to mutate directly their props).


    Bonus: for correctly changing (mutating) the props, you must use "callback fnc prop" in ChildComponent. Right now, it respects well the concept of React Component.

    class ParentComponent extends Component {
      constructor(props) {
        super(props);
        this.state = {
          p1: {a:1, b:2},
      }
    
      this.changeP1 = () => {
         this.setState({p1: {a:3, b:4}});
      }
    
       render() {
          return <ChildComponent p1={this.state.p1} changeP1={this.changeP1} />     
       }
    }