api calls and redux thunk
parent
bf2eef21f2
commit
04d8e062d4
|
@ -0,0 +1,17 @@
|
||||||
|
export async function handleResponse(response) {
|
||||||
|
if (response.ok) return response.json();
|
||||||
|
if (response.status === 400) {
|
||||||
|
// So, a server-side validation error occurred.
|
||||||
|
// Server side validation returns a string error message, so parse as text instead of json.
|
||||||
|
const error = await response.text();
|
||||||
|
throw new Error(error);
|
||||||
|
}
|
||||||
|
throw new Error("Network response was not ok.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// In a real app, would likely call an error logging service.
|
||||||
|
export function handleError(error) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.error("API call failed. " + error);
|
||||||
|
throw error;
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
import { handleResponse, handleError } from "./apiUtils";
|
||||||
|
const baseUrl = process.env.API_URL + "/authors/";
|
||||||
|
|
||||||
|
export function getAuthors() {
|
||||||
|
return fetch(baseUrl).then(handleResponse).catch(handleError);
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
import { handleResponse, handleError } from "./apiUtils";
|
||||||
|
const baseUrl = process.env.API_URL + "/courses/";
|
||||||
|
|
||||||
|
export function getCourses() {
|
||||||
|
return fetch(baseUrl).then(handleResponse).catch(handleError);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function saveCourse(course) {
|
||||||
|
return fetch(baseUrl + (course.id || ""), {
|
||||||
|
method: course.id ? "PUT" : "POST", // POST for create, PUT to update when id already exists.
|
||||||
|
headers: { "content-type": "application/json" },
|
||||||
|
body: JSON.stringify(course)
|
||||||
|
})
|
||||||
|
.then(handleResponse)
|
||||||
|
.catch(handleError);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deleteCourse(courseId) {
|
||||||
|
return fetch(baseUrl + courseId, { method: "DELETE" })
|
||||||
|
.then(handleResponse)
|
||||||
|
.catch(handleError);
|
||||||
|
}
|
|
@ -5,37 +5,14 @@ import PropTypes from "prop-types";
|
||||||
import { bindActionCreators } from "redux";
|
import { bindActionCreators } from "redux";
|
||||||
|
|
||||||
class CoursesPage extends React.Component {
|
class CoursesPage extends React.Component {
|
||||||
state = {
|
|
||||||
course: {
|
|
||||||
title: ""
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
handleChange = (event) => {
|
|
||||||
const course = { ...this.state.course, title: event.target.value };
|
|
||||||
this.setState({ course });
|
|
||||||
};
|
|
||||||
|
|
||||||
handleSubmit = (event) => {
|
|
||||||
event.preventDefault();
|
|
||||||
this.props.actions.createCourse(this.state.course);
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<form onSubmit={this.handleSubmit}>
|
<>
|
||||||
<h2>Courses</h2>
|
<h2>Courses</h2>
|
||||||
<h3>Add Course</h3>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
onChange={this.handleChange}
|
|
||||||
value={this.state.course.title}
|
|
||||||
/>
|
|
||||||
<input type="submit" value="Save" />
|
|
||||||
{this.props.courses.map((course) => (
|
{this.props.courses.map((course) => (
|
||||||
<div key={course.title}>{course.title}</div>
|
<div key={course.title}>{course.title}</div>
|
||||||
))}
|
))}
|
||||||
</form>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
export const CREATE_COURSE = "CREATE_COURSE";
|
export const CREATE_COURSE = "CREATE_COURSE";
|
||||||
|
export const LOAD_COURSES_SUCCESS = "LOAD_COURSES_SUCCESS";
|
||||||
|
|
|
@ -1,5 +1,23 @@
|
||||||
import * as types from "./actionTypes";
|
import * as types from "./actionTypes";
|
||||||
|
import * as courseApi from "../../api/courseApi";
|
||||||
|
|
||||||
export function createCourse(course) {
|
export function createCourse(course) {
|
||||||
return { type: types.CREATE_COURSE, course };
|
return { type: types.CREATE_COURSE, course };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function loadCoursesSuccess(courses) {
|
||||||
|
return { type: types.LOAD_COURSES_SUCCESS, courses };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function loadCourses() {
|
||||||
|
return function (dispatch) {
|
||||||
|
return courseApi
|
||||||
|
.getCourses()
|
||||||
|
.then((courses) => {
|
||||||
|
dispatch(loadCoursesSuccess(courses));
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
throw error;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { createStore, applyMiddleware, compose } from "redux";
|
import { createStore, applyMiddleware, compose } from "redux";
|
||||||
import rootReducer from "./reducers";
|
import rootReducer from "./reducers";
|
||||||
import reduxImmutableStateInvariant from "redux-immutable-state-invariant";
|
import reduxImmutableStateInvariant from "redux-immutable-state-invariant";
|
||||||
|
import thunk from "redux-thunk";
|
||||||
|
|
||||||
export default function configureStore(initialState) {
|
export default function configureStore(initialState) {
|
||||||
const composeEnhancers =
|
const composeEnhancers =
|
||||||
|
@ -9,6 +10,6 @@ export default function configureStore(initialState) {
|
||||||
return createStore(
|
return createStore(
|
||||||
rootReducer,
|
rootReducer,
|
||||||
initialState,
|
initialState,
|
||||||
composeEnhancers(applyMiddleware(reduxImmutableStateInvariant()))
|
composeEnhancers(applyMiddleware(thunk, reduxImmutableStateInvariant()))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,9 @@ module.exports = {
|
||||||
https: false
|
https: false
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
|
new webpack.DefinePlugin({
|
||||||
|
"process.env.API_URL": JSON.stringify("http://localhost:3001")
|
||||||
|
}),
|
||||||
new HtmlWebpackPlugin({
|
new HtmlWebpackPlugin({
|
||||||
template: "src/index.html",
|
template: "src/index.html",
|
||||||
favicon: "src/favicon.ico"
|
favicon: "src/favicon.ico"
|
||||||
|
|
Loading…
Reference in New Issue