Author avatar

Raphael Alampay

Change Props in Stateless React.js Components through Parent Functions

Raphael Alampay

  • Sep 28, 2020
  • 7 Min read
  • 73 Views
  • Sep 28, 2020
  • 7 Min read
  • 73 Views
Web Development
Front End Web Development
Client-side Frameworks
React

Introduction

This guide will analyze the primary role of props in a React.js component, how it is different from state, and when to use it over state. This will be done by seeing how props values are changed within a component as well as how they can be used to update the state of an external component.

Developers sometimes interchange props and state as both can be accessed via the instance of a component and hold data. However, if a component needs to maintain data independent of external components calling it, then state should be used. If a component needs to render data and invoke functions from another component, those data should be passed through props.

Setup

Suppose that you have two components, Display and Controls. Display will maintain all display information of the application. For now, it will be a simple message maintained within the state of Display. To update the message of Display, you will have to invoke its method, updateMessage(message).

Display

Create a component Display which initially looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import React from 'react';

export default class Display extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      message: ""
    }
  }

  updateMessage(message) {
    this.setState({
      message: message
    });
  }

  render() {
    return (
      <div>
        {this.state.message}
      </div>
    );
  }
}
javascript

Notice that all updateMessage(message) is doing is updating the message within Display's state by invoking setState().

Controls

Next, create a Controls component that will contain an input element. Controls will be a stateless component—that is, you will not be maintaining any state values within it. Instead, it will make use of values in props to be rendered within it.

Create a Controls component that looks like the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import React from 'react';

export default class Controls extends React.Component {
  constructor(props) {
    super(props);

    this.state = {}
  }

  render() {
    <div>
      <input
        type="text"
      />
      Message: {this.props.message}
    </div>
  }
}
javascript

Props as Store of Values

Notice that in Controls's constructor, props is passed. This means that props as a set of values are always passed to a component by another component. In this case, that includes values coming from Display towards Controls. Within the constructor, a React.js component calls super(props) to store the passed props as a property of the component (since everything eventually extends React.Component, every component has the props member within it).

Now, in the following line within Controls's render you have:

1
Message: {this.props.message}
jsx

This allows this component to display the values of props that were passed to it—in this case, the message.

Updating Props From Method Calls

How then can you update message within Display? The trick is to update the state of the component calling Display. To do so, you should be able to invoke Display's updateMessage(message) method from Controls by passing it to props as well. You have to first invoke Controls within Display and pass message as well as updateMessage to its props by a set of key value pairs. Import it first at the top part of Display:

1
import Controls from "./Controls";
javascript

This assumes that Controls.js is within the same directory as Display.js.

Next, display Controls within Display's render():

1
2
3
4
5
6
7
8
9
10
11
12
render() {
  return (
    <div>
      {this.state.message}
      <hr/>
      <Controls
        message={this.state.message}
        updateMessage={this.updateMessage.bind(this)}
      />
    </div>
  );
}
javascript

Notice how message and updateMessage become keys that Controls assigned a value and a method, just like passing values of attributes in an HTML element.

Next, the value of the input element of Controls should be bound to the props.message.

1
2
3
4
<input
  type="text"
  value={this.props.message}
/>
jsx

This means that if props.message is updated, the value will displayed within input. But the only way to update the props of Controls if it is passed by the calling component, Display.

Next, create a method within Controls called updateMessage(event) that will be bound to the onChange attribute of the input element.

1
2
3
updateMessage(event) {
  this.props.updateMessage(event.target.value);
}
javascript

Notice that it accepts event, which contains target, which is a reference to the input element it is bound to. The target's value will then be the value that the user put in. You pass this value to the updateMessage invoked from props, which is currently a reference to the updateMessage of the calling component Display. Update Controls's input element to hook in updateMessage:

1
2
3
4
5
<input
  type="text"
  value={this.props.message}
  onChange={this.updateMessage.bind(this)}
/>
jsx

Overall Code

Your final code should look like the following:

Display

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import React from 'react';
import Controls from './Controls';

export default class Display extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      message: ""
    }
  }

  updateMessage(message) {
    this.setState({
      message: message
    });
  }

  render() {
    return (
      <div>
        {this.state.message}
        <hr/>
        <Controls
          message={this.state.message}
          updateMessage={this.updateMessage.bind(this)}
        />
      </div>
    );
  }
}
javascript

Controls

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import React from 'react';

export default class Controls extends React.Component {
  constructor(props) {
    super(props);

    this.state = {}
  }

  updateMessage(event) {
    this.props.updateMessage(event.target.value);
  }

  render() {
    return (
      <div>
        <input
          type="text"
          value={this.props.message}
          onChange={this.updateMessage.bind(this)}
        />
      </div>
    );
  }
}
javascript

As the user enters values in the input element, updateMessage of Controls will be triggered, which in turn calls updateMessage of Display. Since the logic of updateMessage of Display updates its state, it forces a call to render(), which in turn passes the updated message back to Controls via props, which in turn is bound to Controls's input's value.

Conclusion

props are different from state because props values are passed to a component whereas state values are initialized within a component. A component can be stateless and only use the props values passed to it. These values can either contain references to a calling component's state values or references to a calling component's method. props containing a method can invoke that method from the calling component. If the state of a calling component's Display method is updated, the component that is called Controls will receive the updated values, thus updating its props.

1