course list
parent
04d8e062d4
commit
49fbd7be66
|
@ -0,0 +1,43 @@
|
|||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
const CourseList = ({ courses }) => (
|
||||
<table className="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th />
|
||||
<th>Title</th>
|
||||
<th>Author</th>
|
||||
<th>Category</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{courses.map((course) => {
|
||||
return (
|
||||
<tr key={course.id}>
|
||||
<td>
|
||||
<a
|
||||
className="btn btn-light"
|
||||
href={"http://pluralsight.com/courses/" + course.slug}
|
||||
>
|
||||
Watch
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<Link to={"/course/" + course.slug}>{course.title}</Link>
|
||||
</td>
|
||||
<td>{course.authorName}</td>
|
||||
<td>{course.category}</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
|
||||
CourseList.propTypes = {
|
||||
courses: PropTypes.array.isRequired
|
||||
};
|
||||
|
||||
export default CourseList;
|
|
@ -1,17 +1,33 @@
|
|||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import * as courseActions from "../../redux/actions/courseActions";
|
||||
import * as authorActions from "../../redux/actions/authorActions";
|
||||
import PropTypes from "prop-types";
|
||||
import { bindActionCreators } from "redux";
|
||||
import CourseList from "./CourseList";
|
||||
|
||||
class CoursesPage extends React.Component {
|
||||
componentDidMount() {
|
||||
const { courses, authors, actions } = this.props;
|
||||
|
||||
if (courses.length === 0) {
|
||||
actions.loadCourses().catch((error) => {
|
||||
alert("Loading courses failed. " + error);
|
||||
});
|
||||
}
|
||||
|
||||
if (authors.length === 0) {
|
||||
actions.loadAuthors().catch((error) => {
|
||||
alert("Loading authors failed. " + error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
<h2>Courses</h2>
|
||||
{this.props.courses.map((course) => (
|
||||
<div key={course.title}>{course.title}</div>
|
||||
))}
|
||||
<CourseList courses={this.props.courses} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -19,18 +35,35 @@ class CoursesPage extends React.Component {
|
|||
|
||||
CoursesPage.propTypes = {
|
||||
courses: PropTypes.array.isRequired,
|
||||
authors: PropTypes.array.isRequired,
|
||||
actions: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
courses: state.courses
|
||||
courses:
|
||||
state.authors.length === 0
|
||||
? []
|
||||
: state.courses.map((course) => {
|
||||
const author = state.authors.find((a) => a.id === course.authorId);
|
||||
return {
|
||||
...course,
|
||||
authorName: author.name
|
||||
};
|
||||
}),
|
||||
authors: state.authors
|
||||
};
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators(courseActions, dispatch)
|
||||
actions: bindActionCreators(
|
||||
{
|
||||
loadCourses: courseActions.loadCourses,
|
||||
loadAuthors: authorActions.loadAuthors
|
||||
},
|
||||
dispatch
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
export const CREATE_COURSE = "CREATE_COURSE";
|
||||
export const LOAD_COURSES_SUCCESS = "LOAD_COURSES_SUCCESS";
|
||||
export const LOAD_AUTHORS_SUCCESS = "LOAD_AUTHORS_SUCCESS";
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
import * as types from "./actionTypes";
|
||||
import * as authorApi from "../../api/authorApi";
|
||||
|
||||
function loadAuthorsSuccess(authors) {
|
||||
return { type: types.LOAD_AUTHORS_SUCCESS, authors };
|
||||
}
|
||||
|
||||
export function loadAuthors() {
|
||||
return function (dispatch) {
|
||||
return authorApi
|
||||
.getAuthors()
|
||||
.then((authors) => {
|
||||
dispatch(loadAuthorsSuccess(authors));
|
||||
})
|
||||
.catch((error) => {
|
||||
throw error;
|
||||
});
|
||||
};
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
import * as types from "../actions/actionTypes";
|
||||
|
||||
export default function authorReducer(state = [], action) {
|
||||
switch (action.type) {
|
||||
case types.LOAD_AUTHORS_SUCCESS:
|
||||
return action.authors;
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
|
@ -5,6 +5,9 @@ export default function courseReducer(state = [], action) {
|
|||
case types.CREATE_COURSE:
|
||||
return [...state, { ...action.course }];
|
||||
|
||||
case types.LOAD_COURSES_SUCCESS:
|
||||
return action.courses;
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import { combineReducers } from "redux";
|
||||
import courseReducer from "./courseReducer";
|
||||
import authorReducer from "./authorReducer";
|
||||
|
||||
const rootReducer = combineReducers({
|
||||
courses: courseReducer
|
||||
courses: courseReducer,
|
||||
authors: authorReducer
|
||||
});
|
||||
|
||||
export default rootReducer;
|
||||
|
|
Loading…
Reference in New Issue