api calls and redux thunk

master
Tudor Stanciu 2020-04-11 22:09:25 +03:00
parent bf2eef21f2
commit 04d8e062d4
8 changed files with 71 additions and 26 deletions

17
src/api/apiUtils.js Normal file
View File

@ -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;
}

6
src/api/authorApi.js Normal file
View File

@ -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);
}

22
src/api/courseApi.js Normal file
View File

@ -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);
}

View File

@ -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> </>
); );
} }
} }

View File

@ -1 +1,2 @@
export const CREATE_COURSE = "CREATE_COURSE"; export const CREATE_COURSE = "CREATE_COURSE";
export const LOAD_COURSES_SUCCESS = "LOAD_COURSES_SUCCESS";

View File

@ -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;
});
};
}

View File

@ -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()))
); );
} }

View File

@ -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"