Candlejack
Candlejack

Reputation: 445

Unable to render component when using a reducer

I am using React-Redux to make a calendar app that stores gardening tasks by month. When I make use of a reducer to store 'task' objects, I am unable to render them out to the calendar, despite not getting any console errors. When I pass in the name of the task using a string, they render correctly.

I have mostly been debugging by console logging at every point to check that the data being passed around is not undefined and is the correct data type. Everything about my app is as I would expect it to be, and nothing seems wrong with it, except that it will not render.

This does not render tasks to the calendar, but produces no errors:

  render() {
    return (
      <ul className="task-container">
          { this.props.taskTypes.map(task => task.name).forEach(taskName => this.renderTask(taskName)) }
      </ul>
    );
  };

This code works (the tasks render to the calendar as expected):

  render() {
    return (
      <ul className="task-container">
        { this.renderTask("Harvest") }
      </ul>
    );
  };

This is my reducer file (task-types.js):

export default function() {
  return [
    { name: 'Sow Indoors' },
    { name: 'Sow Outside' },
    { name: 'Plant Outside' },
    { name: 'Harvest' },
  ];
}

This is the container file in full (TaskGroup.js):

class TaskGroup extends Component {
  constructor(props) {
    super(props);
    this.checkAgainstMonthAndType = this.checkAgainstMonthAndType.bind(this);
    this.taskTypeIsUsed = this.taskTypeIsUsed.bind(this);
    this.taskHasItems = this.taskHasItems.bind(this);
    this.renderTaskGroup = this.renderTaskGroup.bind(this);
    this.renderTask = this.renderTask.bind(this);
    this.renderTaskItems = this.renderTaskItems.bind(this);
  }

  checkAgainstMonthAndType(list, givenMonth, givenType) {
    return list.some(item => item.month === givenMonth && item.type === givenType);
  }

  taskTypeIsUsed(list, givenMonth, givenType) {
    return list.some(item => this.checkAgainstMonthAndType(item.tasks, givenMonth, givenType));
  };

  taskHasItems(list, givenMonth, givenType) {
    return this.checkAgainstMonthAndType(list, givenMonth, givenType);
  };

  renderTask(taskType) {
    const taskClass = taskType.toLowerCase().replace(" ", "-"); // Sow Indoors -> sow-indoors

    if( this.taskTypeIsUsed(this.props.data, this.props.thisMonth, taskType) ) {
      return (
        <li key={`key-${taskClass}`} className={`task task--${taskClass}`}>
          <h3 className={`task__title task__title--${taskClass}`}>{taskType}</h3>
          <ul className="task__item-list">
            {this.renderTaskItems(taskType)}
          </ul>
        </li>
      );
    }
  };

  renderTaskItems(taskType) {
    return this.props.data
      .filter(item => this.taskHasItems(item.tasks, this.props.thisMonth, taskType))
      .map(item =>
        <Task key={`task-${item.name}`} variety={item.variety} name={item.name} />
      );
  };

  render() {
    return (
      <ul className="task-container">
        {this.renderTask("Sow Indoors")}
      </ul>
    );
  };

}

function mapStateToProps(state) {
  return {
    taskTypes: state.taskTypes,
    data: state.data
  }
}

TaskGroup.propTypes = {
  thisMonth: PropTypes.string.isRequired,
  taskTypes: PropTypes.array.isRequired,
  data: PropTypes.array.isRequired,
};

export default connect(mapStateToProps)(TaskGroup);

The calendar app reads API data whereby each plant is an object in this format:

{
      "id": 0,
      "name": "Peas",
      "variety": "Sugarsnap",
      "tasks": [
        {
          "type": "Sow Indoors",
          "month": "Feb"
        },
        {
          "type": "Plant Outside",
          "month": "Apr"
        },
        {
          "type": "Harvest",
          "month": "May"
        }
      ]
    }

Upvotes: 2

Views: 58

Answers (1)

Hemerson Carlin
Hemerson Carlin

Reputation: 7424

forEach loop doesn't return anything. Use only map instead.

render() {
  return (
    <ul className="task-container">
      {this.props.taskTypes.map(task => this.renderTask(task.name)}
    </ul>
  )
}

Upvotes: 4

Related Questions