[React] Props & States

React passes data between components through States and Props.

Props are read-only data that are passed from one component to another. States represent data that change over time. States can only be used in Class components, not in Function components.

Props

Arguments passed to components are called props. They look just HTML attributes.

class AuthorList extends React.Component {
  render() {
   return ( 
     <div>
       <Author name="Ernest Hemingway" />
       <Author name="Stephen King" /> 
     </div>
   );
  };
}

Props are accessed using the “this.props” object.

class Author extends React.Component {
  render() {
   return ( 
     <p className="author-name">{this.props.name}</p>
   );
  };
}

When to use Props

Props play two big roles:

  • Parent components send data to child components through props
  • Child components accept callback functions as props to communicate back with parent components

Passing Dynamic Data as Props

Let’s try a more real-world-like example.

getAuthors() {
  // call ajax and return this
  return [ 
    {id: 1, name: "Ernest Hemingway"}, 
    {id: 2, name: "Stephen King"} 
  ];
};

In the “AuthorList” component, you can do like this.

getAuthorList() {
  let authorData = this.getAuthors();
  return authorData.map((author) => {
    return (
      <div key={author.id}>
        <Author name={author.name} />
      </div>
    );
  });
}

render() { 
 return (
     <div>{this.getAuthorList()}</div>
   );
};

Rules of Props

The react document says:

“All React components must act like pure functions with respect to their props.”

This means when the props are the same, the output of the component is always the same. You should not modify the props inside the component.

Default Props

import React from 'react';

const MessageBox = (props) => {
  return (
    <div>
      {props.message}
    </div>
  );
};

MessageBox.defaultProps = {
  message: 'Hello...'
}

export default MessageBox;

States

Manipulate DOM

You can manipulate DOM directly (such as jQuery) or indirectly (React).

Using jQuery, you can catch the browser events and directly updates DOM (hide/show elements, modify the text, etc..).

React handles this case differently. Rather than directly modifying DOM, a user updates the states in the event handler, and React updates the DOM (rerenders the component). So, developers do not modify DOM directly.

States

  • A ‘state‘ is a JavaScript object that contains data for a class component.
  • Updating a state causes the component to re-render.
  • A state needs to be initialized when a component is initialized.
  • A state can be updated only through the “setState()” method.

States and Immutability

Each component has a state object you can access. One of the most important features of React is “the State is immutable.” What does it mean by this? You cannot directly modify the state object. The state can only be modified by calling the “setState()” method.

The “setState()” method runs asynchronously, which means this might cause the racing condition. When you update the state from the current state, you need to use the function to return the state from the previous state.

// (1) DON'T !!!
this.state.name = "Test";

// (2) DO THIS 
this.setState({ name: "Test" }); 

// (3) DO THIS
this.setState(prevState => { 
  return { counter: prevState.counter + 1 };	
});

// (4) DO THIS - shortcut to (3)
this.setState(prevState =>
  ({ counter: prevState.counter + 1 })
);

Initial State

You can declare an initial state in the component’s constructor.

class Person extends React.Component {
  constructor(props, context) {
    super(props, context);
    this.state = { name: '' }; // initial
  }
  render() {
    return (
      <div>{this.state.name}</div>
    );
  }
}

Changing States

In general, a user action triggers the state change, and it should be rendered automatically.

Let’s look at the simple example to increment the number when the button is clicked.

class IncrementButton extends React.Component {
  constructor(props, context) {
    super(props, context);
    this.state = { number: 0 }; // initial
    this.handleClick = this.handleClick.bind(this);
  }
  handleClick() {
    this.setState(prevState => ({ number: prevState.number + 1}));
  }
  render() {
    return (
      <div>
        <div>{this.state.number}</div>
        <button className="btn btn-primary" onClick={this.handleClick}>
          Increment
        </button>
      </div>
    );
  }
}

There are a couple of points to look at:

  • A state object is initialized in a constructor
  • An event handler is bound to “this” object using the “bind()” method. -> It is important to make the event handler has the correct ‘this‘ reference
  • A state object is updated using the “setState()” method rather than modifying itself directly

Changing state based on the current state

In the “setState()” method, you can access the state directly. But this can cause the race condition.

this.setState({ number: this.state.number + 1});

Instead, you can get the state value through the argument.

this.setState(prevState => ({ number: prevState.number + 1}));

States and Rendering

When the states are changed, the component needs to be rerendered. React takes care of this process. When you set the new state using setState(), render() will be invoked.


Composition

One powerful feature of React is you can nest components. The special ‘children’ prop is used to pass child elements.

const Product = (props) => {
    return (
      <div>
        <div>{props.name}</div>
        <div>{props.children}</div>
      </div>  
    );
}
<Product name="Milk">
  <ProductDescription />
</Product>
<Product name="Bread">
  <h3>Just baked!</h3> 
</Product>

PropTypes

The dynamic nature of JavaScript types might cause problems when you pass props to components. Checking the type of each member of a props object can be useful. Also, it can be used as documentation to show how to pass the data.

‘prop-types’ Package

You can check the type of each prop member using the “typeof” operator. But there is a better way. The ‘prop-types‘ package provides an easy way to check the types.

Note that the ‘prop-types‘ package is a separate package since React v15.5.

import PropTypes from 'prop-types';

Checking the props for a Component

The special “propTypes” property (which starts with the lower case ‘p’) is used.

And then, you can specify the expected type for each member (prop) of a props object.

  • array
  • bool
  • func
  • number
  • object
  • string
  • symbol
const Message = (props) => (
  <div>{props.id}: {props.text}</div>
);

Message.propTypes = {
  id: PropTypes.number,
  text: PropTypes.string
}

If you pass the wrong type, you can see the warning in the console.

isRequired

By default, a prop is optional. Append ‘isRequired‘ to show a warning if the prop isn’t provided.

Message.propTypes = {
  id: PropTypes.number.isRequired,
  text: PropTypes.string.isRequired
}

Default Prop Values

The special “defaultProps” property is used to define default values for props.

const Message = (props) => (
  <div>{props.text}</div>
);

Message.propTypes = {
  text: PropTypes.string
}

Message.defaultProps = {
  text: 'Hello!'
}

static propTypes

In the class React component, you can use the static “propTypes” property inside the class definition.

class Message extends React.Component {        
  static propTypes = {
    text: PropTypes.string.isRequired
  }

  render() {
    return (
      <div>{this.props.text}</div>
    );
  }
}

Leave a Comment

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s