How to reduce boilerplate - javascript

I have store structure like this
/store
/entities
users.js
sessions.js
All of my entities looks like this
import { FORM_ERROR } from 'react-final-form';
import { normalize, schema } from 'normalizr';
import { SCHEMA as USER_SHEMA } from './users';
export const SCHEMA = new schema.Entity(
'sessions',
{ user: USER_SHEMA },
{ idAttribute: 'sessionGuid' },
);
export const CREATE_SESSION_REQUEST = 'CREATE_SESSION_REQUEST';
export const CREATE_SESSION_SUCCESS = 'CREATE_SESSION_SUCCESS';
export const CREATE_SESSION_FAILURE = 'CREATE_SESSION_FAILURE';
export const create = session => async (dispatch, getState, api) => {
dispatch({ type: CREATE_SESSION_REQUEST });
try {
const { data } = await api.post('/sessions', session);
const payload = normalize(data, SCHEMA);
dispatch({ type: CREATE_SESSION_SUCCESS, payload });
} catch (error) {
dispatch({ type: CREATE_SESSION_FAILURE, payload: error, error: true });
return { [FORM_ERROR]: error };
}
return null;
};
export const FETCH_ALL_SESSIONS_REQUEST = 'FETCH_ALL_SESSIONS_REQUEST';
export const FETCH_ALL_SESSIONS_SUCCESS = 'FETCH_ALL_SESSIONS_SUCCESS';
export const FETCH_ALL_SESSIONS_FAILURE = 'FETCH_ALL_SESSIONS_FAILURE';
export const fetchAll = params => async (dispatch, getState, api) => {
dispatch({ type: FETCH_ALL_SESSIONS_REQUEST });
try {
const { data } = await api.get('/sessions', { params });
const payload = normalize(data, [SCHEMA]);
dispatch({ type: CREATE_SESSION_SUCCESS, payload });
} catch (error) {
dispatch({ type: FETCH_ALL_SESSIONS_FAILURE, payload: error, error: true });
}
};
export const GET_ONE_SESSION_REQUEST = 'GET_ONE_SESSION_REQUEST';
export const GET_ONE_SESSION_SUCCESS = 'GET_ONE_SESSION_SUCCESS';
export const GET_ONE_SESSION_FAILURE = 'GET_ONE_SESSION_FAILURE';
export const fetchOne = sessionId => async (dispatch, getState, api) => {
dispatch({ type: GET_ONE_SESSION_REQUEST });
try {
const { data } = await api.get(`/sessions/${sessionId}`);
const payload = normalize(data, SCHEMA);
dispatch({ type: GET_ONE_SESSION_SUCCESS, payload });
} catch (error) {
dispatch({ type: GET_ONE_SESSION_FAILURE, payload: error, error: true });
}
};
export const UPDATE_SESSION_REQUEST = 'UPDATE_SESSION_REQUEST';
export const UPDATE_SESSION_SUCCESS = 'UPDATE_SESSION_SUCCESS';
export const UPDATE_SESSION_FAILURE = 'UPDATE_SESSION_FAILURE';
export const update = (sessionId, session) => async (dispatch, getState, api) => {
dispatch({ type: UPDATE_SESSION_REQUEST, meta: { id: sessionId } });
try {
await api.put(`/sessions/${sessionId}`, session);
dispatch({ type: UPDATE_SESSION_SUCCESS, meta: { id: sessionId } });
} catch (error) {
dispatch({
type: UPDATE_SESSION_FAILURE,
error: true,
payload: error,
meta: { id: sessionId },
});
return { [FORM_ERROR]: error };
}
return null;
};
export const DESTROY_SESSION_REQUEST = 'DESTROY_SESSION_REQUEST';
export const DESTROY_SESSION_SUCCESS = 'DESTROY_SESSION_SUCCESS';
export const DESTROY_SESSION_FAILURE = 'DESTROY_SESSION_FAILURE';
export const destroy = sessionId => async (dispatch, getState, api) => {
dispatch({ type: DESTROY_SESSION_REQUEST, meta: { id: sessionId } });
try {
await api.delete(`/sessions/${sessionId}`);
dispatch({ type: DESTROY_SESSION_SUCCESS, meta: { id: sessionId } });
} catch (error) {
dispatch({
type: DESTROY_SESSION_FAILURE,
error: true,
payload: error,
meta: { id: sessionId },
});
}
};
I wanna use import * as ns from './ns' syntaxys in my components, so i export my actionTypes like as constants. In users.js the same code, but all the all words session are replaced with users. My project now has 12 entities, and when I replaced all the keywords in the 13th file, I remembered the DRY principle. But I did not find the right way to reduce the template code in such a way that would not change the imports.
import * as sessionsActions from 'store/modules/entities/sessions';
How to minimize the boilerplate by saving the current code?

Related

I'm getting API Error when I try to login after added next.js to my react app

I migrated and added Next.js to my React app. I getting the following error when I try to login. When I checked seems that I have to use promise.all. I tried different solutions without success. I want to know how it works. Your help and advice are highly appreciated.
error message;
Unhandled Runtime Error
Error: Objects are not valid as a React child (found: [object Promise]). If you meant to render a collection of children, use an array instead.
src/action/auth.js;
import axios from 'axios';
import { setAlert } from './alert';
// import { API_URL } from '../config/index';
import {
LOGIN_SUCCESS,
LOGIN_FAIL,
SIGNUP_SUCCESS,
SIGNUP_FAIL,
ACTIVATION_SUCCESS,
ACTIVATION_FAIL,
USER_LOADED_SUCCESS,
USER_LOADED_FAIL,
AUTHENTICATED_SUCCESS,
AUTHENTICATED_FAIL,
PASSWORD_RESET_SUCCESS,
PASSWORD_RESET_FAIL,
PASSWORD_RESET_CONFIRM_SUCCESS,
PASSWORD_RESET_CONFIRM_FAIL,
LOGOUT
} from './types';
export const checkAuthenticated = () => async dispatch => {
if (typeof window !== 'undefined' ? window.localStorage.getItem('access') : false) {
const config = {
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
};
const body = JSON.stringify({ token: typeof window !== 'undefined' ? window.localStorage.getItem('access') : false });
try {
const res = await axios.post(`${process.env.NEXT_PUBLIC_API_URL}/auth/jwt/verify/`, body, config)
if (res.data.code !== 'token_not_valid') {
dispatch({
type: AUTHENTICATED_SUCCESS
});
} else {
dispatch({
type: AUTHENTICATED_FAIL
});
}
} catch (err) {
dispatch({
type: AUTHENTICATED_FAIL
});
}
} else {
dispatch({
type: AUTHENTICATED_FAIL
});
}
};
export const load_user = () => async dispatch => {
if (typeof window !== 'undefined' ? window.localStorage.getItem('access') : false) {
const config = {
headers: {
'Content-Type': 'application/json',
'Authorization': `JWT ${typeof window !== 'undefined' ? window.localStorage.getItem('access') : false}`,
'Accept': 'application/json'
}
};
try {
const res = await axios.get(`${process.env.NEXT_PUBLIC_API_URL}/auth/users/me/`, config);
dispatch({
type: USER_LOADED_SUCCESS,
payload: res.data
});
}catch (err) {
dispatch({
type: USER_LOADED_FAIL
});
}
} else {
dispatch({
type: USER_LOADED_FAIL
});
}
};
export const login = (email, password) => async dispatch => {
const config = {
headers: {
'Content-Type': 'application/json'
}
};
const body = JSON.stringify({ email, password });
try {
const res = await axios.post(`${process.env.NEXT_PUBLIC_API_URL}/auth/jwt/create/`, body, config);
dispatch({
type: LOGIN_SUCCESS,
payload: res.data
});
dispatch(setAlert('Authenticated successfully', 'success'));
dispatch(load_user());
}catch (err) {
dispatch({
type: LOGIN_FAIL
});
dispatch(setAlert('Error Authenticating', 'error'));
}
};
export const signup = (name, email, password, re_password) => async dispatch => {
const config = {
headers: {
'Content-Type': 'application/json'
}
};
const body = JSON.stringify({ name, email, password, re_password });
try {
const res = await axios.post(`${process.env.NEXT_PUBLIC_API_URL}/auth/users/`, body, config);
dispatch({
type: SIGNUP_SUCCESS,
payload: res.data
});
dispatch(setAlert('Check Your Email to Activate Your Account.', 'warning'));
} catch (err) {
dispatch({
type: SIGNUP_FAIL
})
}
};
export const verify = (uid, token) => async dispatch => {
const config = {
headers: {
'Content-Type': 'application/json'
}
};
const body = JSON.stringify({ uid, token });
try {
await axios.post(`${process.env.NEXT_PUBLIC_API_URL}/auth/users/activation/`, body, config);
dispatch({
type: ACTIVATION_SUCCESS,
});
dispatch(setAlert('Account Activated Successfully.', 'success'));
} catch (err) {
dispatch({
type: ACTIVATION_FAIL
})
}
};
//Reset Password
export const reset_password = (email) => async dispatch => {
const config = {
headers: {
'Content-Type': 'application/json'
}
};
const body = JSON.stringify({ email });
try {
await axios.post (`${process.env.NEXT_PUBLIC_API_URL}/auth/users/reset_password/`, body, config);
dispatch({
type: PASSWORD_RESET_SUCCESS
});
dispatch(setAlert('Check Your Email to Rest Password.', 'warning'));
} catch (err) {
dispatch({
type: PASSWORD_RESET_FAIL
});
}
};
// Reset Password Confirm
export const reset_password_confirm = (uid, token, new_password, re_new_password) => async dispatch => {
const config = {
headers: {
'Content-Type': 'application/json'
}
};
const body = JSON.stringify({ uid, token, new_password, re_new_password });
try {
await axios.post (`${process.env.NEXT_PUBLIC_API_URL}/auth/users/reset_password_confirm/`, body, config);
dispatch(setAlert('Password Rest Successful.', 'success'));
dispatch({
type: PASSWORD_RESET_CONFIRM_SUCCESS
});
} catch (err) {
dispatch({
type: PASSWORD_RESET_CONFIRM_FAIL
});
}
};
//Logout
export const logout = () => dispatch => {
dispatch(setAlert('Logout successful.', 'success'));
dispatch({
type: LOGOUT
});
};
src/pages/login.js;
import React, { useState } from 'react';
import Link from 'next/link';
import { useRouter } from 'next/router';
import { connect } from 'react-redux';
import { Button } from '#mui/material';
import { login } from '../actions/auth';
import styles from '../styles/Login.module.css';
import Head from 'next/head';
import WelcomePageFooter from '../components/WelcomePageFooter';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import { useTranslation } from 'next-i18next';
import i18n from '../../i18n';
function Login({ login, isAuthenticated }) {
const { t } = useTranslation();
const navigate = useRouter();
const [formData, setFormData] = useState({
email: '',
password: ''
});
const { email, password } = formData;
const onChange = e => setFormData({ ...formData, [e.target.name]: e.target.value});
const onSubmit = e => {
e.preventDefault();
login (email, password)
};
if (isAuthenticated) {
return (
navigate.replace('/')
);
}
return (
<main>
<div className={styles.login}>
<Head>
<title>Diploman - Login</title>
<meta
name='description'
content='login page'
/>
</Head>
<h1 className={styles.login__title}>{t('login_title')}</h1>
<p className={styles.login__lead}>{t('login_lead')}</p>
<form className={styles.login__form} onSubmit={e => onSubmit(e)}>
<div className={styles.login__form__group}>
<input
className={styles.login__form__input}
type='email'
placeholder={t('Form_email')}
name='email'
value={email}
onChange={e => onChange(e)}
required
/>
</div>
<div className={styles.login__form__group}>
<input
className={styles.login__form__input}
type='password'
placeholder={t('Form_pw')}
name='password'
value={password}
onChange={e => onChange(e)}
minLength='8'
required
/>
</div>
<Button className={styles.login__button__main} type='submit'>{t('login_title')}</Button>
</form>
<p className={styles.link__to__Signup}>
{t('login_text1')} <Link href='/signup' className={styles.login__link}>{t('login_register')}</Link>
</p>
<p className={styles.link__to__resetPassword}>
{t('login_text2')} <Link href='/reset-password' className={styles.reset__password__link}>{t('login_reset')}</Link>
</p>
</div>
<WelcomePageFooter/>
</main>
)
};
export const getServerSideProps = async ({ locale }) => (
{ props: {
...(await serverSideTranslations(
locale,
['common'],
i18n,
)),
} }
);
const mapStateToProps = state => ({
isAuthenticated: state.auth.isAuthenticated
});
export default connect (mapStateToProps, { login }) (Login);
I really appreciate your help here

how can i use async await in useEffect when i use reactnative?

When useEffect is executed, I want to get the token through AsyncStorage, then get the data value through the axios.post ('/auth/me') router and execute the KAKAOLOG_IN_REQUEST action with disaptch.
As a result of checking the data value with console.log, the data value came in well. But when I run my code, this error occurs.
Possible Unhandled Promise Rejection (id: 1):
Error: Actions may not have an undefined "type" property. Have you misspelled a constant?
Error: Actions may not have an undefined "type" property. Have you misspelled a constant?
how can i fix my code?....
this is my code
(index.js)
const App = ({}) => {
const dispatch = useDispatch();
useEffect(() => {
async function fetchAndSetUser() {
const token = await AsyncStorage.getItem('tokenstore', (err, result) => {
});
var {data} = await axios.post(
'/auth/me',
{},
{
headers: {Authorization: `Bearer ${token}`},
},
);
console.log("data:",data);
dispatch({
type: KAKAOLOG_IN_REQUEST,
data: data,
});
}
fetchAndSetUser();
}, []);
return <Navigator />;
};
export {App};
(reducer/user.js)
import {
KAKAOLOG_IN_FAILURE,
KAKAOLOG_IN_REQUEST,
KAKAOLOG_IN_SUCCESS,
} from '../reducers/user';
function* watchkakaoLogIn() {
yield takeLatest(KAKAOLOG_IN_REQUEST, kakaologIn);
}
function* kakaologIn(action) {
try {
// const result = yield call(kakaologInAPI, action.data);
yield put({
type: KAKAOLOG_IN_SUCCESS,
data: action.data,
});
} catch (err) {
console.error(err);
yield put({
type: KAKAOLOG_IN_FAILURE,
error: err.response.data,
});
}
}
export default function* userSaga() {
yield all([
fork(watchkakaoLogIn),
]);
}
(reducer/index.js)
import { combineReducers } from 'redux';
import user from './user';
import post from './post';
// (이전상태, 액션) => 다음상태
const rootReducer = (state, action) => {
switch (action.type) {
// case HYDRATE:
// // console.log('HYDRATE', action);
// return action.payload;
default: {
const combinedReducer = combineReducers({
user,
post,
});
return combinedReducer(state, action);
}
}
};
export default rootReducer;
(src/index.js)
import {KAKAOLOG_IN_REQUEST} from '../sagas/user';
const App = ({}) => {
const dispatch = useDispatch();
useEffect(() => {
async function fetchAndSetUser() {
try {
const token = await AsyncStorage.getItem('tokenstore');
const {data} = await axios.post(
'/auth/me',
{},
{
headers: {Authorization: `Bearer ${token}`},
},
);
console.log('data::::::', data);
dispatch({
type: 'KAKAOLOG_IN_REQUEST',
data: data,
});
} catch (error) {
}
}
fetchAndSetUser();
}, []);
return <Navigator />;
};
export {App};
Issue
The error message is saying your code can throw an error and it isn't handled. It is also saying that KAKAOLOG_IN_REQUEST is undefined for some reason (perhaps you forgot to import it, or it is really a string).
Solution
Surround your asynchronous code in a try/catch. Define KAKAOLOG_IN_REQUEST or pass as a string "KAKAOLOG_IN_REQUEST".
useEffect(() => {
async function fetchAndSetUser() {
try {
const token = await AsyncStorage.getItem('tokenstore');
const {data} = await axios.post(
'/auth/me',
{},
{
headers: { Authorization: `Bearer ${token}` },
},
);
console.log("data:",data);
dispatch({
type: 'KAKAOLOG_IN_REQUEST',
data: data,
});
} catch(error) {
// handle error, logging, etc...
}
}
fetchAndSetUser();
}, []);

How to test a recursive dispatch in Redux

I implemented my own way to handle access/refresh token. Basically when accessToken is expired, it awaits the dispatch of another action and, if it is successful, it dispatch again itself. The code below explains it better:
export const refresh = () => async (dispatch) => {
dispatch({
type: REFRESH_USER_FETCHING,
});
try {
const user = await api.refresh();
dispatch({
type: REFRESH_USER_SUCCESS,
payload: user,
});
return history.push("/");
} catch (err) {
const { code } = err;
if (code !== "ACCESS_TOKEN_EXPIRED") {
dispatch({
type: REFRESH_USER_ERROR,
payload: err,
});
const pathsToRedirect = ["/signup"];
const {
location: { pathname },
} = history;
const path = pathsToRedirect.includes(pathname) ? pathname : "/login";
return history.push(path);
}
try {
await dispatch(refreshToken());
return dispatch(refresh());
} catch (subErr) {
dispatch({
type: REFRESH_USER_ERROR,
payload: err,
});
return history.push("/login");
}
}
};
export const refreshToken = () => async (dispatch) => {
dispatch({
type: REFRESH_TOKEN_FETCHING,
});
try {
await api.refreshToken();
dispatch({
type: REFRESH_TOKEN_SUCCESS,
});
} catch (err) {
dispatch({
type: REFRESH_TOKEN_ERROR,
payload: err,
});
}
};
the issue is that I am finding it really difficult to test with Jest. In fact, I have implemented this test:
import configureMockStore from "redux-mock-store";
import thunk from "redux-thunk";
import * as actionCreators from "./actionCreators";
import * as actions from "./actions";
import api from "../../api";
jest.mock("../../api");
const middlewares = [thunk];
const mockStore = configureMockStore(middlewares);
describe("authentication actionCreators", () => {
it("runs refresh, both token expired, should match the whole flow", async () => {
api.refresh.mockRejectedValue({
code: "ACCESS_TOKEN_EXPIRED",
message: "jwt expired",
});
api.refreshToken.mockRejectedValue({
code: "REFRESH_TOKEN_EXPIRED",
message: "jwt expired",
});
const expectedActions = [
{ type: actions.REFRESH_USER_FETCHING },
{ type: actions.REFRESH_TOKEN_FETCHING },
{ type: actions.REFRESH_TOKEN_ERROR },
{ type: actions.REFRESH_USER_ERROR },
];
const store = mockStore({ auth: {} });
await store.dispatch(actionCreators.refresh());
expect(store.getActions()).toEqual(expectedActions);
});
});
but instead of completing, the test runs indefenitely. This issue is not happening when I am testing it manually, so I think there is something missing in Jest, so my question is: is there a way to test this recursive behaviour?
Thanks
The problem is await you use with dispatch, dispatch returns an action, not a Promise, use Promise.resolve instead.

React. Can't acces API data on local 5000. Database from CSV file. Promise undefined response

I've been going crazy for a couple of days with an unresolved issue. Please help.
Working with a CSV database here. Creating own API. Small React App Fullstack MERN.
I'm trying to get to show all the employeees from my database.
I wrote the backend with express and have all the data now showing in json format on localhost:5000/employees
Also created a context on the front to deal with that data and reducer file for the functions.
Problem I have is I'm not beeing able to use the data on the ContextProvider file. ('EmployeesState')
Promise of my async function keeps giving me an undefined response. Tried both with fetch and axios but is not working.
Some of the code is from a previous project i did and it worked there so i 'm going crazy here. I can't seem to solve it. Please help.
BACKEND
db.js
const parse = require("csv-parse");
const fs = require("fs");
const employeesData = [];
const connectDB = () => {
fs.createReadStream(__dirname + "/employees1.txt")
.pipe(
parse({
delimiter: ",",
columns: true,
})
)
.on("data", (dataRow) => {
employeesData.push(dataRow);
})
.on("end", () => {
console.log(employeesData);
});
};
connectDB();
// console.log(connectDB());
module.exports = connectDB;
module.exports = employeesData;
server.js
var createError = require("http-errors");
var express = require("express");
const bodyParser = require("body-parser");
var cookieParser = require("cookie-parser");
const logger = require("morgan");
const cors = require("cors");
const connectDB = require("./config/db");
const employeesData = require("./config/db");
var path = require("path");
var app = express();
// const config = require("config");
//Connect DB
// connectDB();
let dataBase = employeesData;
console.log(employeesData);
app.use(cors());
// app.use(bodyParser.json()); //here
// Initial Middleware
//By doing this we can accepta data. Using the req.body
app.use(express.json({ extended: false }));
///Define Routes
app.use("/", require("./routes/index"));
app.use("/employees", require("./routes/employees"));
//Serve React in production
if (process.env.NODE_ENV === "production") {
//Set static folder (build folder)
app.use(express.static("client/build"));
app.get("*", (req, res) =>
res.sendFile(path.resolve(__dirname, "client", "build", "index.html"))
);
}
// app.engine("html", require("ejs").renderFile);
app.set("view engine", "html");
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`Server started on port ${PORT}`));
employees.js
const express = require("express");
const router = express.Router();
const config = require("config");
const { check, validationResult } = require("express-validator");
const Employee = require("../models/EmployeeModel");
const employeesData = require("../config/db");
// router.get("/", function (req, res) {
// res.send({ employeesData });
// });
//#route GET api/employees
//#desc Get all users Employees
//#access Private
router.get("/", async (req, res) => {
try {
const employees = await employeesData;
res.json({ employees });
} catch (err) {
console.error(err.message);
res.status(500).send("Server error");
}
});
//#route POST api/employees
//#desc Add new Employee
//#access Private
router.post(
"/",
[
check("name", "Name is required").not().isEmpty(),
check("surname", "Surname is required").not().isEmpty(),
],
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const { name, surname, adress, phone, email, birthdate } = req.body;
try {
const newEmployee = new Employee({
name,
surname,
adress,
phone,
email,
birthdate,
});
const employee = await newEmployee.save();
res.json(employee);
} catch (err) {
console.error(err.message);
res.status(500).send("Server Error");
}
}
);
//#route DELETE api/employee
//#desc Delete Employee
router.delete("/:id", async (req, res) => {
try {
let employee = await Employee.findById(req.params.id);
if (!employee) return res.status(404).json({ msg: "Employee not found" });
await Employee.findByIdAndRemove(req.params.id);
res.json({ msg: `Employee removed` });
} catch (err) {
console.error(err.message);
res.status(500).send("Server Error");
}
});
module.exports = router;
FRONTEND
EmployeesState.js
import React, { useReducer, useState } from "react";
import axios from "axios";
import { v4 as uuid } from "uuid";
import EmployeesContext from "./employeesContext";
import employeesReducer from "./employeesReducer";
import {
ADD_EMPLOYEE,
DELETE_EMPLOYEE,
SET_CURRENT,
CLEAR_CURRENT,
UPDATE_EMPLOYEE,
FILTER_EMPLOYEES,
EMPLOYEE_ERROR,
CLEAR_FILTER,
GET_EMPLOYEES,
CLEAR_EMPLOYEES,
} from "../types";
const EmployeesState = (props) => {
const initialState = {
employees: [],
current: null,
filtered: null,
error: null,
loading: false,
};
const [state, dispatch] = useReducer(employeesReducer, initialState);
const [employees, setEmployees] = useState(initialState);
//Get Employees
// // gives me promise{<prending>} on console
const getEmployees = async () => {
try {
const res = axios.get("http://localhost:5000/employees");
dispatch({ type: GET_EMPLOYEES, payload: res });
} catch (err) {
dispatch({
type: EMPLOYEE_ERROR,
});
}
};
//trying with fetch. throwing me undefined on console
const callAPI = () => {
fetch("http://localhost:5000/employees")
.then((res) => res.json())
.then((res) =>
setEmployees({
...initialState,
loading: false,
employees: res,
})
);
};
//Add Employee
const addEmployee = async (employee) => {
const config = {
headers: {
"Content-Type": "application/json",
},
};
try {
const res = await axios.post("/employees", employee, config);
dispatch({ type: ADD_EMPLOYEE, payload: res.data });
} catch (err) {
dispatch({
type: EMPLOYEE_ERROR,
payload: err.response.data.msg,
});
}
};
//Delete Employee
const deleteEmployee = async (id) => {
try {
await axios.delete(`/employees/${id}`);
dispatch({ type: DELETE_EMPLOYEE, payload: id });
} catch (err) {
dispatch({
type: EMPLOYEE_ERROR,
payload: err.response.msg,
});
}
};
//Update Employee
const updateEmployee = async (employee) => {
const config = {
headers: {
"Content-Type": "application/json",
},
};
try {
const res = await axios.put(
`/api/employees/${employee._id}`,
employee,
config
);
dispatch({ type: UPDATE_EMPLOYEE, payload: res.data });
} catch (err) {
dispatch({
type: EMPLOYEE_ERROR,
payload: err.response.msg,
});
}
dispatch({ type: UPDATE_EMPLOYEE, payload: employee });
};
//Clear Employees
const clearEmployees = () => {
dispatch({ type: CLEAR_EMPLOYEES });
};
//Set Current Employees
const setCurrent = (employee) => {
dispatch({ type: SET_CURRENT, payload: employee });
};
//Clear Current Employee
const clearCurrent = () => {
dispatch({ type: CLEAR_CURRENT });
};
//Filter Employees
const filterEmployees = (text) => {
dispatch({ type: FILTER_EMPLOYEES, payload: text });
};
//Clear Filter
const clearFilter = (text) => {
dispatch({ type: CLEAR_FILTER });
};
return (
<EmployeesContext.Provider
value={{
employees: state.employees,
current: state.current,
filtered: state.filtered,
error: state.error,
loading: state.loading,
callAPI,
getEmployees,
addEmployee,
deleteEmployee,
clearEmployees,
setCurrent,
clearCurrent,
updateEmployee,
filterEmployees,
clearFilter,
}}
>
{props.children}
</EmployeesContext.Provider>
);
};
export default EmployeesState;
// // gives me undefined on console
// const getEmployees = () => {
// axios
// .get("http://localhost:5000/employees")
// .then((res) => dispatch({ type: GET_EMPLOYEES, payload: res }))
// .catch((err) => {
// dispatch({
// type: EMPLOYEE_ERROR,
// });
// });
// };
Home.js
import React, { Fragment } from "react";
import Search from "../employees/Search";
import Employees from "../employees/Employees";
const Home = () => {
return (
<>
<Search />
<Employees />
</>
);
};
export default Home;
Employees.js
import React, { useContext, useEffect } from "react";
import { CSSTransition, TransitionGroup } from "react-transition-group";
import EmployeeItem from "./EmployeeItem";
import { v4 as uuidv4 } from "uuid";
import EmployeesContext from "../../contexts/employees/employeesContext";
import Spinner from "../layout/Spinner";
const Employees = () => {
const employeesContext = useContext(EmployeesContext);
const {
employees,
filtered,
getEmployees,
callAPI,
loading,
} = employeesContext;
useEffect(() => {
// callAPI();
getEmployees();
// eslint-disable-next-line
console.log(employees);
console.log(getEmployees());
console.log(callAPI());
}, []);
return (
<div>
<>
{[employees].map((employee) => (
<EmployeeItem key={uuidv4()} employee={employee} />
))}
</>
</div>
);
employeesReducer.js
import {
GET_EMPLOYEES,
ADD_EMPLOYEE,
DELETE_EMPLOYEE,
SET_CURRENT,
CLEAR_CURRENT,
UPDATE_EMPLOYEE,
FILTER_EMPLOYEES,
CLEAR_FILTER,
EMPLOYEE_ERROR,
CLEAR_EMPLOYEES,
} from "../types";
export default (state, action) => {
switch (action.type) {
case GET_EMPLOYEES:
return {
...state,
employees: action.payload,
loading: false,
};
case ADD_EMPLOYEE:
return {
...state,
employee: [action.payload, ...state.employees],
loading: false,
};
case UPDATE_EMPLOYEE:
return {
...state,
employees: state.employees.map((employee) =>
employee._id === action.payload._id ? action.payload : employee
),
loading: false,
};
case DELETE_EMPLOYEE:
return {
...state,
employees: state.employees.filter(
(employee) => employee._id !== action.payload
),
loading: false,
};
case CLEAR_EMPLOYEES:
return {
...state,
employees: null,
filtered: null,
error: null,
current: null,
};
case SET_CURRENT:
return {
...state,
current: action.payload,
};
case CLEAR_CURRENT:
return {
...state,
current: null,
};
case FILTER_EMPLOYEES:
return {
...state,
filtered: state.employees.filter((employee) => {
const regex = new RegExp(`${action.payload}`, "gi");
return employee.name.match(regex) || employee.email.match(regex);
}),
};
case CLEAR_FILTER:
return {
...state,
filtered: null,
};
case EMPLOYEE_ERROR:
return {
...state,
error: action.payload,
};
default:
return state;
}
};
You need to await for the response
const res = await axios.get("http://localhost:5000/employees");
dispatch({ type: GET_EMPLOYEES, payload: res });
or
axios.get("http://localhost:5000/employees")
.then(res => {
dispatch({ type: GET_EMPLOYEES, payload: res });
})

Redux-thunk with promise does not work

I am trying to chain dispatches with redux-thunk. I have 2 action creator as below:
getResourceLinks:
export const getResourceLinks = () => {
return dispatch => {
let req = {
url: getRootUrl(),
header: {
Accept: 'application/json'
}
};
return request(req).then(res => {
dispatch({
type: ActionTypes.RESOURCE.LOAD_URL_SUCCESS,
payload: res.body
});
}).catch(err => {
dispatch({
type: ActionTypes.RESOURCE.LOAD_URL_ERROR,
payload: err
});
});
}
};
and loadAppliances:
export const loadAppliances = () => {
return (dispatch, getState) => {
return dispatch(getResourceLinks()).then(res => {
const {resources} = getState();
let req = {
url: getResourceLink(Resources.Appliances, res.body),
header: {
Accept: 'application/json'
}
};
request(req).then(res1 => {
dispatch({
type: ActionTypes.APPLIANCE.LOAD_SUCCESS,
payload: res1.body
});
}).catch(err => {
dispatch({
type: ActionTypes.APPLIANCE.LOAD_ERROR,
payload: err
});
});
});
};
};
I am facing with an error: Uncaught TypeError: Cannot read property 'then' of undefined at line 3 in loadAppliances action. Promise was returned correctly, wasn't it? Am I doing wrong something? I've seen carefully examples of thunk-redux but I don't still find out what was wrong.
Update. Here is request:
import superagent from 'superagent';
import superagentPromisePlugin from 'superagent-promise-plugin';
import {RequestMethods} from '../constant';
const request = ({url, method = RequestMethods.GET, param, body, header}) => {
let methodStr;
switch (method) {
case RequestMethods.POST:
methodStr = 'POST';
break;
case RequestMethods.PUT:
methodStr = 'PUT';
break;
case RequestMethods.DELETE:
methodStr = 'DELETE';
break;
default:
methodStr = 'GET';
break;
}
let req = superagent(methodStr, url).use(superagentPromisePlugin);
//set header
if (header) {
req.set(header)
}
//set param
if (param) {
req.query(param)
}
//set body
if (body) {
req.send(body)
}
return req;
};
export default request;
The problem here is that dispatch does not return your promise. It actually returns the dispatched action itself. (reference).
return dispatch(getResourceLinks()).then(res => {
^--- this is the problem
The way I would approach this is to dispatch an action after your first successful call and store any pertinent information in the state, then dispatch the next call and store its response.
Example
const getResourceLinks = () => {
return request({
url: getRootUrl(),
header: {
Accept: 'application/json'
}
});
};
const getAppliances = (appliances) => {
return request({
url: getResourceLink(Resources.Appliances, appliances),
header: {
Accept: 'application/json'
}
})
};
export const loadAppliances = () => {
return (dispatch, getState) => {
getResourceLinks()
.then(res => {
dispatch({
type: ActionTypes.RESOURCE.LOAD_URL_SUCCESS,
payload: res.body
});
return getAppliances(res.body)
.then(res1 => {
dispatch({
type: ActionTypes.APPLIANCE.LOAD_SUCCESS,
payload: res1.body
});
})
.catch(err => {
dispatch({
type: ActionTypes.APPLIANCE.LOAD_ERROR,
payload: err
});
});
})
.catch(err => {
dispatch({
type: ActionTypes.RESOURCE.LOAD_URL_ERROR,
payload: err
});
});
}
}
You also might want to take a look at redux-saga

Categories