Reputation: 1832
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
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:
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
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
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
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