57

Why do we have both state and props? Why don't we just have one source of data? I'd like to update a component's props and have it re-render itself and all of its children. Seems simple but I can't figure out how to let a component update its own or its parent's props.

Thanks for any help.

boom
  • 10,856
  • 9
  • 43
  • 64
  • `this.refs` is immutable as well. Throws `Uncaught TypeError: Cannot add property, object is not extensible` – Green Mar 21 '18 at 11:17

6 Answers6

91

The React philosophy is that props should be immutable and top-down. This means that a parent can send whatever prop values it likes to a child, but the child cannot modify its own props. What you do is react to the incoming props and then, if you want to, modify your child's state based on incoming props.

So you don't ever update your own props, or a parent's props. Ever. You only ever update your own state, and react to prop values you are given by parent.

If you want to have an action occur on a child which modifies something on the state, then what you do is pass a callback to the child which it can execute upon the given action. This callback can then modify the parent's state, which in turns can then send different props to the child on re-render.

ruffin
  • 16,507
  • 9
  • 88
  • 138
Mike Driver
  • 8,481
  • 3
  • 36
  • 37
  • Ok, this touches on my main question. What if a component gets a touch event and needs up update some data that a parent component relies on? Do children have a reference to their parent? What if it's not an immediate parent? How do you update the props of a parent once you have a reference to it? Thanks – boom Sep 28 '14 at 21:12
  • 4
    Just like you might pass an `onChange` callback or similar down to a React builtin child like ``. You do the same for your own children. Make the child accept an `onTouch` (or whatever you want to call it) property, and have the child execute `this.props.onTouch` in the touch event handler within the child. The parent should set `onTouch={this.childTouchHandler}` or something similar, to deal with this. So the parent should always deal with it's own state, however it can react to "custom" events and handle callbacks it sends to children. – Mike Driver Sep 28 '14 at 21:18
  • If a component has a list of items (say products) to display, should the products be its props or state? It will have to get those items from server via ajax. I'm guessing it should be state since a component can't alter its props when it receives new set of data from server. But making the products as state doesn't sound right. Any thoughts? – Pratheep Oct 11 '15 at 18:02
  • @Pratheep putting data in the state which is fetched within the component itself is totally fine. – Mike Driver Oct 11 '15 at 18:32
  • @Pratheep, you might find those values belong in a store (which is a singleton) so that the values are fetched locally (if recent enough) and fetched via AJAX if not. – boatcoder Jan 24 '17 at 19:28
  • Also known as Lifting Up State. There's a tutorial explaining this in the docs. It's useful if someone's looking for more information on this. – Raunaqss Jul 19 '17 at 07:58
  • I am wondering what happens if my `prop` is a reference type like `Array` or `Object`, and I set the child component's initial `state` equal to this `prop`, and then modify it using `setState`? Will the prop's value change in this case? And what if the `prop` has deeply nested objects/arrays? Will changing them in `setState` change the `props`'s value? – darKnight Mar 31 '20 at 06:23
29

To answer the question of why

In React, props flow downward, from parent to child.

This means that when we call ReactDOM.render, React can render the root node, pass down any props, and then forget about that node. It's done with. It's already rendered.

This happens at each component, we render it, then move on down the tree, depth-first.

If a component could mutate its props, we would be changing an object that is accessible to the parent node, even after the parent node had already rendered. This could cause all sorts of strange behaviour, for example, a user.name might have one value in one part of the app, and a different value in a different part, and it might update itself the next time a render is triggered.

To give a fictional example:

// App renders a user.name and a profile
const App = (props) => 
  React.createElement('div', null, [
    props.user.name,
    React.createElement(Profile, props)
  ])

// Profile changes the user.name and renders it
// Now App has the wrong DOM.
const Profile = ({user}) => {
  user.name = "Voldemort" // Uh oh!
  return React.createElement('div', null, user.name);
}

// Render the App and give it props
ReactDOM.render(
  React.createElement(App, {user: {name: "Hermione"}}), 
  document.getElementById('app'))
);

We render app. It outputs "Hermione" to the Shadow DOM. We render the Profile, it outputs "Voldemort". The App is now wrong. It should say "Voldemort" because user.name is "Voldemort", but we already output "Hermione", and it's too late to change it.

The value will be different in different parts of the app.

Modifying Props would be two-way-binding

Mutating props would be a form of two-way binding. We would be modifying values that might be relied on by another component higher up the tree.

Angular 1 had this, you could change any data anytime from wherever you were. In order to work, it needed a cyclical $digest. Basically, it would loop around and around, re-rendering the DOM, until all the data had finished propagating. This was part of the reason why Angular 1 was so slow.

abdul-wahab
  • 2,182
  • 3
  • 21
  • 35
superluminary
  • 47,086
  • 25
  • 151
  • 148
13

In React, state and props serve different goals: state allows a component to maintain some changing values, while props are the mecanism to propagate those values to children.

Children are not allowed to alter by themselves the values they get via props just because React designers find it easier to maintain an application built this way. Their point is that when only one component is allowed to update some piece of state, it is easier to discover who altered it, and find the root of bugs.

Valéry
  • 4,574
  • 1
  • 14
  • 25
10

the Component itself changes its state, and changes not its own, but the children's props.

<Parent>
  <Child name={ this.state.childName } />
</Parent>

Parent can change its own state and change the child name, but it will change the props for his children.

edit1: for calling events from the child to its parent, you should pass in the child an event handler like so:

var Child = React.createClass({
  render: function() {
    return (<button onClick={ this.props.onClick }>Hey</button>);
  }
});

var Parent = React.createClass({
  onChildClick: console.log.bind(console), // will print the event..
  render: function() {
    return (<Child onClick={ this.onChildClick } />);
  }
});

React.renderComponent(<Parent />, document.body);

in this code, when you'll click on the Child's button, it will pass the event to its parent. the purpose of passing the events is decoupling the components. maybe in your app you need this specific action, but in another app you'll have, you'll use it differently.

Gal Schlezinger
  • 364
  • 1
  • 8
0

My solution was fairly different but some people might run into it. On the Chrome Dev tools, it kept saying that my props were read-only and when I tried passing them down even further, I would get an error. Now, the reason why is because I wasn't invoking a render() method. I was instead calling my component like this:

const Navigation = () =>{

    return (
        <div className="left-navigation">
            <ul>
                <Link to='/dashboard'><li>Home</li></Link>
                <Link to='/create-seedz'><li>Create Seedz</li></Link>
                <Link to='/create-promotion'><li>Create Promotion</li></Link>
                <Link to='/setting'><li>Setting</li></Link>
                <SignOutButton  />
            </ul>
        </div>
    );
}

I added a render method and it solved my issue of being able to pass props down:

class Navigation extends Component{
render(){
    return (
        <div className="left-navigation">
            <ul>
                <Link to='/dashboard'><li>Home</li></Link>
                <Link to='/create-seedz'><li>Create Seedz</li></Link>
                <Link to='/create-promotion'><li>Create Promotion</li></Link>
                <Link to='/setting'><li>Setting</li></Link>
                <SignOutButton user={this.props.user} signedOut={this.props.signedOut} authed={this.props.authed}/>
            </ul>
        </div>
    );
}
}

Hopefully this helps someone.

Lewis Menelaws
  • 1,186
  • 5
  • 20
  • 42
-20

Contrary to the answers provided here, you actually can update props directly, if you don't mind defying the pedantic circlejerk about "the React way." In React.js, find the following lines of code:

Object.freeze(element.props);
Object.freeze(element);

and comment them out. Voila, mutable props!

joeyp
  • 111
  • 2
  • 28
    modifying the react core in order to implement an anti-pattern is a really bad practice. – orourkedd Dec 15 '15 at 23:45
  • 6
    So what happens say, when I update my version of React? Or say, what if I'm getting React from a CDN? Or I do some work for someone who doesn't have access my fantastically modified version of React? Or I want to use a plugin/component/piece of functionality someone else built? You say 'defying the pedantic circlejerk', most everyone else is going to say 'it is a terrible idea to modify the core functionality of a library do not have control of' – DanCouper Feb 18 '16 at 13:55
  • 1
    "it is a terrible idea to modify the core functionality of a library do not have control of" - well said – Sauce Feb 26 '16 at 15:17
  • 3
    pedantic: "overly concerned with minute details or formalisms". What you're describing breaks the core rule of React... not such a minute detail. – Andrew Oct 20 '16 at 10:45
  • 6
    This got 19 down votes. But I think this is by far the most interesting answer. Question, how do you trigger a ui re-render if you do it this way? – Dave Ford May 03 '17 at 23:47
  • 1
    What you're talking about here is two-way binding. Angular 1 had this, and needed a cyclical digest. Basically, it rerendered the DOM over and over until all the data had finished propagating. This is one of the main reasons why Angular 1 was so slow. – superluminary Jun 01 '17 at 14:08