Ryszard Bosiak
Ryszard Bosiak

Reputation: 75

React and PropTypes

Some time ago I tried to make my first steps in React. Now I'm trying to create simple ToDo list app. One of my colleagues told me that it will be nice to use PropTypes with shape and arrayOf. I have one component which pass data to another and here is my issue(List.js and ListItem.js). I'm not sure when, where and how to use propTypes with shape and arrayOf. Which approach is right? Define PropTypes in parent component or in child component? Maybe there is a better idea? Please find my code below, it will be nice if you can clarify me how to use PropTypes. I tried to search something in Web, but I still don't get it.

Main App.js file:

import React from "react";
import "./styles/App.scss";
import List from "./components/List/List";
import AppHeader from "./components/AppHeader/AppHeader";

function App() {
  return (
    <div className="App">
      <AppHeader content="TODO List"></AppHeader>
      <List></List>
    </div>
  );
}

export default App;

AppHeader.js file:

import React from "react";
import "./AppHeader.scss";

export default function AppHeader(props) {
  return (
    <div className="header">
      <h1 className="header-headline">{props.content}</h1>
    </div>
  );
}

List.js file:

import React from "react";
import "./List.scss";
import ListItem from "../ListItem/ListItem";
import _ from "lodash";

const initialList = [
  { id: "a", name: "Water plants", status: "done" },
  { id: "b", name: "Buy something to eat", status: "in-progress" },
  { id: "c", name: "Book flight", status: "in-preparation" }
];

const inputId = _.uniqueId("form-input-");

// function component
const List = () => {
  const [value, setValue] = React.useState(""); // Hook
  const [list, setList] = React.useState(initialList); // Hook

  const handleChange = event => {
    setValue(event.target.value);
  };

  // add element to the list
  const handleSubmit = event => {
    // prevent to add empty list elements
    if (value) {
      setList(
        list.concat({ id: Date.now(), name: value, status: "in-preparation" })
      );
    }

    // clear input value after added new element to the list
    setValue("");

    event.preventDefault();
  };

  // remove current element from the list
  const removeListItem = id => {
    setList(list.filter(item => item.id !== id));
  };

  // adding status to current element of the list
  const setListItemStatus = (id, status) => {
    setList(
      list.map(item => (item.id === id ? { ...item, status: status } : item))
    );
  };

  return (
    <div className="to-do-list-wrapper">
      <form className="to-do-form" onSubmit={handleSubmit}>
        <label htmlFor={inputId} className="to-do-form-label">
          Type item name:
        </label>
        <input
          type="text"
          value={value}
          onChange={handleChange}
          className="to-do-form-input"
          id={inputId}
        />
        <button type="submit" className="button to-do-form-button">
          Add Item
        </button>
      </form>

      <ul className="to-do-list">
        {list.map(item => (
          <ListItem
            name={item.name}
            status={item.status}
            key={item.id}
            id={item.id}
            setListItemStatus={setListItemStatus}
            removeListItem={removeListItem}
          ></ListItem>
        ))}
      </ul>
    </div>
  );
};

export default List;

And ListItem.js file:

import React from "react";
import PropTypes from "prop-types";
import "./ListItem.scss";

const ListItem = props => {
  return (
    <li className={"list-item " + props.status} key={props.id}>
      <span className="list-item-icon"></span>
      <div className="list-item-content-wrapper">
        <span className="list-item-text">{props.name}</span>
        <div className="list-item-button-wrapper">
          <button
            type="button"
            onClick={() => props.setListItemStatus(props.id, "in-preparation")}
            className="button list-item-button"
          >
            In preparation
          </button>
          <button
            type="button"
            onClick={() => props.setListItemStatus(props.id, "in-progress")}
            className="button list-item-button"
          >
            In progress
          </button>
          <button
            type="button"
            onClick={() => props.setListItemStatus(props.id, "done")}
            className="button list-item-button"
          >
            Done
          </button>
          <button
            type="button"
            onClick={() => props.removeListItem(props.id)}
            className="button list-item-button"
          >
            Remove
          </button>
        </div>
      </div>
    </li>
  );
};

ListItem.propTypes = {
  id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
  name: PropTypes.string,
  status: PropTypes.string,
  removeListItem: PropTypes.func,
  setListItemStatus: PropTypes.func
};

export default ListItem;

Upvotes: 0

Views: 494

Answers (2)

user9572013
user9572013

Reputation:

Props are per component.

The purpose of prop types is to let you make some type checking on a component's props.

It seems like you did it correctly in the ListItem component.

Basically, you just list all of the component's props and what type they should be.

Like you did here:

ListItem.propTypes = {
  id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
  name: PropTypes.string,
  status: PropTypes.string,
  removeListItem: PropTypes.func,
  setListItemStatus: PropTypes.func
};

Upvotes: 2

wentjun
wentjun

Reputation: 42526

I am not exactly sure where will the propTypes shape and arrayOf be used, but generally, PropTypes are useful when you are trying to render a child component within a parent component, and you want to carry out TypeChecking on the props of the relative child component.

In your scenario, arrayOf and shape can be used in one of the props within your component whereby the prop is an array of a certain type (arrayOf), and an object of a certain type(shape, or exact).

Upvotes: 1

Related Questions