rantao
rantao

Reputation: 1832

React Error: Nothing was returned from render

Note: I already looked through similar questions; the solutions provided do not solve my problem.

I am getting the following error in my React app

TasksColumn(...): Nothing was returned from render. This usually >means a return statement is missing. Or, to render nothing, return >null.

Here is the relevant code:

TasksColumn.js

import React, { Component } from "react";
import { render } from "react-dom";
import { Col, Row } from "react-bootstrap";

const taskData = [
  {
    title: "Preliminary Report",
    course: "Capstone Design",
    type: "Report",
    deadline: "2019-10-04"
  },
  {
    title: "title2",
    course: "course2",
    type: "type2",
    deadline: "date2"
  },
  {
    title: "title3",
    course: "course3",
    type: "type3",
    deadline: "deadline3"
  }
];

const TaskRecord = ({ title, course, type, deadline }) => {
  return (
    <Row class="task-record">
      <Col>
        <div class="task-record-title">
          <h5>{title}</h5>
        </div>
        <div class="task-record-course">
          <h6>{course}</h6>
        </div>
      </Col>
      <Col>
        <div class="task-record-type">
          <p>{type}</p>
        </div>
        <div class="task-record-deadline">
          <p>{deadline}</p>
        </div>
      </Col>
    </Row>
  );
};

export default class TasksColumn extends Component {
  constructor(props) {
    super(props);
    this.state = {
      isLoading: false,
      mounted: false,
      data: []
    };
  }
  componentDidMount() {
    this.setState({
      isLoading: true,
      mounted: true
    });
  }
  render() {
    const { tasks } = this.props;
    let mounted = this.state.mounted;

    if (mounted == true) {
      return (
        <Col id="tasks-column">
          {tasks.map((task, i) => (
            <TaskRecord
              key={i}
              title={task.title}
              course={task.course}
              type={task.type}
              deadline={task.deadline}
            />
          ))}
        </Col>
      );
    }
  }
}

render(<TasksColumn tasks={taskData} />, document.querySelector("#root"));

Dashboard.js

import React from "react";
import { Container, Row, Col } from "reactstrap";
import TasksColumn from "../../molecules/TasksColumn";

export default function Dashboard(props) {
  return (
    <React.Fragment>
      <Container>
        <Row>
          <TasksColumn />
        </Row>
      </Container>
    </React.Fragment>
  );
}

As an end result, I need the TasksColumns in Dashboard.js to render each taskData inside a TaskRecord.

Upvotes: 2

Views: 6899

Answers (4)

goto
goto

Reputation: 4445

Your problem is the TaskColumn class, specifically the render method:

class TaskColumn extends React.Component {
  render() {
    const mounted = this.state.mounted

    // DON'T DO THIS
    if (mounted == true) {
      return (...)
    }
  }
}

According to the React.js docs:

componentDidMount() is invoked immediately after a component is mounted (inserted into the tree)

Since your render function only returns the JSX when mounted is true, essentially you are returning undefined when mounted is false. This happens when your component mounts for the first time (before componentDidMount gets triggered) and is wrong as the render function has to return any of the following:

  • React elements
  • Arrays and fragments
  • Portals
  • String and numbers
  • Booleans or null

Have a look here to read more: reactjs.org/docs/react-component.html#render.

If you don't want to render anything before you meet a certain condition, you should just return null:

class TaskColumn extends React.Component {
  render() {
    const { tasks } = this.props
    const isLoading = this.state.isLoading

    if (isLoading) {
      return <h1>Loading</h1>
    }
    if (!isLoading && tasks && tasks.length > 0) {
      return (
        <Col>
         {tasks.map(...)}
        </Col>
      )
    }
    // otherwise return null if you don't want to 
    // show anything until data/tasks become available
    return null
  }
}

Upvotes: 3

Mr.eggplant
Mr.eggplant

Reputation: 48

You should pass taskData as props to taskColumn where you call it, ie in Dashboard. This is better practice because this way, the "caller" component controls its children component. Having it set up this way is cleaner and more readable. React is like this all the way - all about modularity and breaking things down and keeping data flows clean. Read here for more: https://reactjs.org/docs/thinking-in-react.html.

You shouldn't need

render (
    <TasksColumn tasks={taskData}/>, 
    document.querySelector("#root")
)

outside a Component, since you now have all these components set up with their individual render methods.

Other notes: You probably don't need two state variables for 'mounted' / 'loading' for your use case so far. Just use PropTypes to set defaults (or make required) taskData. Example:

TasksColumn.propTypes = {
  taskData: PropTypes.arrayOf(PropTypes.shape({
    title: PropTypes.string,
    course: PropTypes.string,
    type: PropTypes.string,
    deadline: PropTypes.string,
  }))
};

TasksColumn.defaultProps = {
  taskData: [
    {
      title: '',
      course: '',
      type: '',
      deadline: '',
    },
  ],
};

Upvotes: 0

Cat_Enthusiast
Cat_Enthusiast

Reputation: 15698

In your TaskColumn component, currently you are only returning something when mounted is true. But mounted isnt set to true until your logic in componentDidMount(). componentDidMount() only is triggered after the first render. You still need to return something for the initial render when mounted is still false:

export default class TasksColumn extends Component {
     constructor(props) {
          super(props);
          this.state = {
               isLoading: false,
           mounted: false,
           data: []
          };
     }
     componentDidMount() {
          this.setState({
           isLoading: true,
        mounted: true
          });
     }
     render() {
          const { tasks } = this.props;
      let mounted = this.state.mounted;

          if (mounted == true) {
           return ( 
                <Col id="tasks-column">
                 {tasks.map((task, i) => 
                  <TaskRecord 
                       key={i}
                   title={task.title} 
                       course={task.course} 
                   type={task.type}
                   deadline={task.deadline}
                  />
                 )}
             </Col>
           );  
         } else {
            return <div>woof</div>
         }
     }
}

Upvotes: 1

Anil Kumar
Anil Kumar

Reputation: 2309

You are using render name for react DOM which is conflicting with render method.

import ReactDOM from 'react-dom';

ReactDOM.render (
    <TasksColumn tasks={taskData}/>, 
    document.querySelector("#root")
);

Upvotes: -1

Related Questions