Props: Component Arguments page

Learn how to make components reusable with props.

Props Are Like Arguments

Props are the same as arguments you would pass into a function. For example, suppose we had an add method which adds two numbers together. It might look something like this:

function addOneAndSeven() {
  return 1 + 7;
}

This is a perfectly good function, but notice that it always adds the same two numbers. In other words, it's only usable in very specific cases.

We can make this function more reusable by allowing arguments to be passed in:

function addNumbers(num1, num2) {
  return num1 + num2;
}

The function is now infinitely more reusable because the caller can specify the numbers, instead of just having them be hardcoded.

This same principle applies to React components.

Basic Props Usage

React components are just functions which return an element. And like normal functions, we run into the same reusability problems.

function AddNumbers() {
  return <div>{1 + 7}</div>;
}

Take a look at the AddNumbers component above. It returns a div with the result of adding 1 + 7. This is fine, but suppose once again we wanted to specify which numbers it adds.

We can solve this by modifying the AddNumbers component so that it accepts props for num1 and num2.

So we would change AddNumbers so that it would look like this.

function AddNumbers({ num1, num2 }) {
  return <div>{num1 + num2}</div>;
}

In React, all functional components receive a props object as their first argument.

We're also destructuring the props object, a convention used by much of the community and in the rest of this guide.

The props object contains any values which are passed into the component when it's rendered.

<AddNumbers num1={1} num2={7} />
<AddNumbers num1={5} num2={-90} />

Whenever the AddNumbers component is rendered, we can now pass it num1 & num2 props which it will use to do the calculation.

Now, instead of having a hardcoded addition, the component is flexible and can be reused in any scenario where it's necessary to display the result of adding two numbers.

Prop Data Types

Props can be any data type or data structure. Anything you could pass into a JavaScript function can be passed into a component as a prop.

<Component
  string="string props can be passed using quotation marks"
  interpolatedString={`The value is: ${value}`}
  array={[1, 2, 3]}
/>

Props that are static strings can be passed using quotation marks, while non-string props must be passed in a set of curly brackets; this includes interpolated strings.

Callback Props

Just like normal functions, React components can accept props of any data type (even other components). One of the most useful types of props are callbacks. Callback props allow us to specify what a component will do when an action occurs inside of it. Below we have a button that takes an onClick handler that runs every time you click the button.

function MyButton() {
  return <button onClick={() => console.log('clicked')}>click me</button>;
}

The button above calls console.log('clicked') whenever the user clicks it. But once again, we're running into a reusability issue.

Suppose we wanted to perform an arbitrary action when the button is clicked instead of the hardcoded console.log('clicked').

To accomplish that, we would use a callback prop. To do so, simply pass the component a callback function as one of its props and pass that function to the button as the onClick prop. Note that by convention, callback props should begin with on just like native DOM events:

function MyButton({ onButtonClick }) {
  // here we're destructuring the props object
  return <button onClick={onButtonClick}>click me</button>;
}

Notice above that the MyButton component now accepts an onButtonClick prop. Now whenever the button is clicked it will call the callback function, making the MyButton component more reusable.

<MyButton onButtonClick={() => console.log('custom click action')}>

When we render the button, we'll pass in the onButtonClick prop just like the numbers from the AddNumbers component. Since we're not passing a string prop, it needs to be enclosed in curly brackets { }.

Try it out

Below is the MyButton component code in a working example:

function MyButton({ onButtonClick }) {
  return <button onClick={onButtonClick}>click me</button>;
}

function AddNumbers({ num1, num2, isCool }) {
  if (isCool) {
    console.log('This component rocks');
  }

  return <div>{num1 + num2}</div>;
}

function App() {
  return (
    <div>
      <AddNumbers num1={5} num2={10} isCool />
      <MyButton onButtonClick={() => console.log('you clicked')} />
    </div>
  );
}

ReactDOM.render(<App />, document.getElementById('root'));

Notice that if you supply a prop with no value (ie... the isCool in <AddNumbers num1={5} num2={10} isCool/>), then React supplies the value of true. It assumes it is a boolean prop.

Exercise

Let's use our props knowledge to start building out the Tic-Tac-Toe component.

The problem

The goal of this exercise to render a full Tic-Tac-Toe game board. But this time, we will pass the symbol (X, or O) into the Tic-Tac-Toe for rendering. Also we will pass a function down into the Tic-Tac-Toe cell that prints console.log() whenever the user clicks on a square. You will need to render the squares first. This will require you to modify some of the props in the starter code below.

Instructions

  • Modify the Square component so that accepts two props:
    • onClick - a callback which is executed when the user click on it
    • symbol - a string indicating what symbol is in the square (X, O or nothing)
  • Modify the Board component so it renders out all 9 squares given to it by its board props (board is an array of strings).
  • Modify the Game component so it passes the correct props into <Board />.
const squareStyling = {
  width: '200px',
  height: '200px',
  border: '1px solid black',
  boxSizing: 'border-box',
  display: 'flex',
  justifyContent: 'center',
  alignItems: 'center',
  fontSize: '6em',
};

const boardStyling = {
  display: 'flex',
  flexWrap: 'wrap',
  flexDirection: 'row',
  justifyContent: 'flex-start',
  width: '600px',
  height: '600px',
  boxShadow: '0px 3px 8px 0 rgba(0, 0, 0, 0.1)',
  boxSizing: 'border-box',
};

// Square will receive 2 props, "onClick" and "symbol"
// Try destructuring the "props" object below to get them
function Square(props) {
  return (
    <div
      style={squareStyling}
      // add an onClick handler that calls the onClick
      // prop that was passed in
    >
      {/* display the "symbol" prop here */}
    </div>
  );
}

function Board({ onSquareClick, board }) {
  return <div style={boardStyling}>{/*
          Use the map function to loop over the "board" prop.
          Each item in the board array should be mapped to
          a <Square /> component.

          The Square component takes a symbol prop (x or o), an
          onClick prop, and since we're using the map function each
          Square will also need a unique key prop.
      */}</div>;
}

const blankBoard = ['', '', '', '', '', '', '', '', ''];

function Game() {
  return (
    <>
      <Board
        board={[]} // What should actually go here?
        onSquareClick={() => {}}
        // ^ Create a function to pass into onSquareClick
        // that prints out "Clicked"
      />
      current player: X
    </>
  );
}

ReactDOM.render(<Game />, document.getElementById('root'));

You can run the code above by hovering over the code block and hitting Run, which is in the upper right hand corner. Only check the solution below once you've taken a pass yourself.

Solution

Click to see the solution

const squareStyling = {
  width: '200px',
  height: '200px',
  border: '1px solid black',
  boxSizing: 'border-box',
  display: 'flex',
  justifyContent: 'center',
  alignItems: 'center',
  fontSize: '6em',
};

const boardStyling = {
  display: 'flex',
  flexWrap: 'wrap',
  flexDirection: 'row',
  justifyContent: 'flex-start',
  width: '600px',
  height: '600px',
  boxShadow: '0px 3px 8px 0 rgba(0, 0, 0, 0.1)',
  boxSizing: 'border-box',
};

// Square will receive 2 props, "onClick" and "symbol"
// Try destructuring the "props" object below to get them
function Square({ onClick, symbol }) {
  return (
    <div style={squareStyling} onClick={onClick}>
      {symbol}
    </div>
  );
}

function Board({ onSquareClick, board }) {
  return (
    <div style={boardStyling}>
      {board.map((symbol, index) => (
        <Square
          key={index}
          symbol={symbol}
          onClick={() => onSquareClick(index)}
        />
      ))}
    </div>
  );
}

const blankBoard = ['', '', '', '', '', '', '', '', ''];

function Game() {
  const handleSquareClick = () => {
    console.log('Clicked');
  };

  return (
    <>
      <Board board={blankBoard} onSquareClick={handleSquareClick} />
      current player: X
    </>
  );
}

ReactDOM.render(<Game />, document.getElementById('root'));

Next Steps

✏️ Head over to the next lesson to learn all the different ways one can style components.