Error trying to get action & reducer working - javascript

I am following a blog post, attempting to get a user login implemented in my app
https://auth0.com/blog/secure-your-react-and-redux-app-with-jwt-authentication/
It recommends doing the following in my action
// actions.js
export const LOGIN_REQUEST = 'LOGIN_REQUEST'
function requestLogin(creds) {
return {
type: LOGIN_REQUEST,
isFetching: true,
isAuthenticated: false,
creds
}
}
// .....
export function loginUser(creds) {
let config = {
method: 'POST',
headers: { 'Content-Type':'application/x-www-form-urlencoded' },
body: `username=${creds.username}&password=${creds.password}`
}
console.log(creds) // <-- this works
return dispatch => {
console.log(creds) // <-- this doesnt work and everything after it doesnt work
// We dispatch requestLogin to kickoff the call to the API
dispatch(requestLogin(creds))
return fetch('http://localhost:3001/sessions/create', config)
.then(response =>
response.json().then(user => ({ user, response }))
).then(({ user, response }) => {
if (!response.ok) {
// If there was a problem, we want to
// dispatch the error condition
dispatch(loginError(user.message))
return Promise.reject(user)
} else {
// If login was successful, set the token in local storage
localStorage.setItem('id_token', user.id_token)
localStorage.setItem('id_token', user.access_token)
// Dispatch the success action
dispatch(receiveLogin(user))
}
}).catch(err => console.log("Error: ", err))
}
}
However, as soon as the function hits the part return dispatch => { an error gets raised
--- Edit ---
the answers below fixed the console errors, but a new error was introduced
my redux chrome plugin no longer sees my redux store

It seems that you did not set up Redux Thunk which is a Redux library that allows you to have asynchronous actions.
In the tutorial you linked, it is explained how to set it up:
import thunkMiddleware from 'redux-thunk'
let createStoreWithMiddleware = applyMiddleware(thunkMiddleware, api)(createStore)
You should read more about Redux Thunk here.

What you are trying to do here is to utilise a thunk, which is a function that returns another function.
In redux that is used to cause side effects during action creation such as asynchronous calls as part of an action, and producing secondary actions, like dispatching the response from this asynchronous request.
If you look in the blog post package.json they have dependency on redux-thunk middleware package (https://www.npmjs.com/package/redux-thunk) you will need this too, and to attach it to your store...
import { createStore, applyMiddleware} from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';
const store = createStore(
  rootReducer,
  applyMiddleware(thunk)
);

Related

get data from dropdown for dynamic call api

So I use redux-hooks for state management, so in my application there is a dropdown, the value will be used to call api dynamically, in the action creator I made it like this
export const getFetchCasesSelectedCountry = (country) => (dispatch) => {
return (
axios.get(`https://covid19.mathdro.id/api/countries/${country}`).then((result) => {
let confirmed = result.data.confirmed.value;
let recovered = result.data.recovered.value;
let death = result.data.deaths.value;
dispatch({
type: GET_SELECTED_COUNTRY,
payload: {
countryConfirmed: confirmed,
countryRecovered: recovered,
countryDeath: death,
}
})
})
)
}
but i got this
error
how to get the value from the dropdown so that it can be entered into the action creator? is it possible? sorry if my question is hard to understand.
There can be various reasons for 404 issue:
it can be networking issue - I mean that requested URL is not accessible from your environment. I see from the code that you doing GET request so to test networking you can just open your browser with the URL that is being used in the action. https://covid19.mathdro.id/api/countries/USA for example.
Code that calls getFetchCasesSelectedCountry provides some weird country value that can result in a 404 error
Nevertheless the code that you've posted, does not handle errors that can arise from axios call (404 for example) so your store won't be aware of errors that can happen, hence component that is connected to the store also won't be aware of such problems.
From my experience, usual approach to handle such things with redux is to introduce more states, that will store error info:
// this is purely an example
// let say that you have such state
const state = {
loading: false,
hasError: false,
errorMessage: '',
data: null,
}
// reducer
function stateReducer(state, action) {
switch(action.type) {
case GET_SELECTED_COUNTRY_LOAD:
// set here 'loading' to true - started request execution
// return new state
case GET_SELECTED_COUNTRY_LOADED:
// set here 'loading' to false - got response
// set here data that came with action payload
// return new state
case GET_SELECTED_COUNTRY_FAILED:
// set here 'loading' to false - got error response or failed
// sethere 'errorMessage' that came with action payload
// return new state
}
}
// you'll have to create 2 new action creators to handle GET_SELECTED_COUNTRY_LOAD // and GET_SELECTED_COUNTRY_FAILED
// now your code should look like this
const getFetchCasesSelectedCountry = (country) => (dispatch) => {
return (
dispatch({ type: GET_SELECTED_COUNTRY_LOAD });
axios.get(`https://covid19.mathdro.id/api/countries/${country}`)
.then((result) => {
// do some stuff with result
dispatch({
type: GET_SELECTED_COUNTRY_LOADED,
payload: { /* useful data here */ }
});
).catch(err => {
dispatch({
type: GET_SELECTED_COUNTRY_FAILED,
payload: { /* useful error data here */ }
});
})
}
So whenever error happens component that is connected to store will be able to handle it (at least show errorMessage that is can get from the store)
The HTTP error 404, or more commonly called "404 error", means that the api you are trying to use could not be found on the server. This is a client-side incident which means either the endpoint has been deleted or moved, and the URL has not been modified accordingly, or that you have misspelled the URL.
read this for more information

Error handling API calls with axios Interceptors. Am I doing this right?

Hello I'am completly new with React/Redux so there is a possibility that I violated some principles with the below code , so bare with me.
I'm building a React App which will consume my Express API. Everything is working perfectly but when I was building the Action Creators I couldnt think of a good way to handle any errors coming from the API without wrapping every single axios request with try/catch blocks.
Both in PHP world where I come from and Express you can create a global Error handler.
For any async requests in my Express APP I wrap them with the below function so I can catch them the same way as the synchronous.
module.exports = (fn) => {
return (req, res, next) => {
fn(req, res, next).catch((err) => next(err));
};
};
From what I've learned through googling is that, there is an ErrorBoundary HOC for handling errors inside Components and for axios calls I should use axios interceptors. So I created this:
AxiosFactory Class
import axios from "axios";
import { setError } from "../actions/utilActions";
import store from "../store";
class AxiosFactory {
constructor(baseURL) {
this.instance = axios.create({
baseURL,
});
this.instance.interceptors.response.use(
function (response) {
// Any status code that lie within the range of 2xx cause this function to trigger
// Do something with response data
return response;
},
function (error) {
// Any status codes that falls outside the range of 2xx cause this function to trigger
// Do something with response error
// Getting the errors from Express API
const {
response: {
data: { errors },
},
} = error;
store.dispatch(setError(errors));
return Promise.reject(error);
}
);
}
getInstance() {
return this.instance;
}
}
export default AxiosFactory;
User API Caller
import AxiosFactory from './AxiosFactory';
const axios = new AxiosFactory('/api/v1/users/').getInstance();
export default axios;
User ActionCreator
import { SUCCESS_LOGIN } from "./types/userTypes";
import userApi from "../apis/user";
// Tries to login the user
export const signInUser = () => {
return async (dispatch) => {
// Test
const {data:{data:{user} = await userApi.post("login", {
email: "test#test.com",
password: "test12345!",
});
dispatch({
type: SUCCESS_LOGIN,
payload: user,
});
}
Error ActionCreator
import { HAS_ERROR } from "./types/utilTypes";
export const setError = (errors) => {
return async (dispatch) => {
dispatch({
type: HAS_ERROR,
payload: errors,
});
};
};
The interceptor dispatches succesfuly the setError and the error state is getting updated like a charm, which means I dont need to manual dispatch on each call. Although I still need to catch the Promise rejection from Interceptor.
My 2 questions are:
Is there a way to lets say "stop the dispatch from executing" inside my User ActionCreator without try/catching the Promise ?
Does this whole thing I setup makes sense ? Or there is a better way to do it?

React Redux is not working as expected with Next.js & NodeJS

I'm working on an app using Next.js with redux by following this example and here is some part of store.js
// REDUCERS
const authReducer = (state = null, action) => {
switch (action.type){
case actionTypes.FETCH_USER:
return action.payload || false;
default:
return state;
}
}
export const rootReducer = combineReducers({
user: authReducer,
form: reduxForm,
});
// ACTIONS
export const fetchUser = () => {
return (dispatch) => {
axios.get('/api/current_user')
.then(res => dispatch({
type: actionTypes.FETCH_USER,
payload: res.data
}));
};
};
export const submitLogin = (values) => async dispacth => {
const res = await axios.post('/api/login', values);
// Router.push('/');
// console.log(res)
dispacth({ type: actionTypes.SUBMIT_LOGIN, payload: res.data });
};
and the client side such as header
function mapStateToProps (state) {
const { user } = state
return { user }
}
export default connect(mapStateToProps)(Header)
and when I console.log('############=>', this.props.user); the props & I'm not loggesd in then it's showing null but showing some extra data such as below screenshot
but when I logged in & console.log('############=>', this.props.user); it's showing proper data without extra data such as below image
what I'm doing wrong? please help me. Thanks
The problem is probably not on your React / Redux code but on your Next.js routes.
You’re trying to call an API with axios.get('/api/current_user') but Next.js is giving you an HTML response, that you indeed store in authReducer extracting it as action.payload.
You probably want to see this section about Custom Server and Routing.
dispacth({ type: actionTypes.SUBMIT_LOGIN, payload: res.data });
Should be:
dispatch({ type: actionTypes.SUBMIT_LOGIN, payload: res.data });
#MunimMunna is spot on. Your server is either redirecting you to an HTML login page, or returning an HTML error page for failed creds. In either case, Axios is seeing a 200 status code, so it thinks the response is valid. Your action creator blindly fires off the action with the HTML payload attached.
Consider making these changes:
Client:
Add a catch block to your axios promise that logs failed response.
Pass an Accept header of application/json to tell the server you don't want HTML responses. If you are lucky, this might be enough to get NextJS to behave the way you want.
Server: If needed, change the server to detect whether the request is an XHR request, or if application/json is the only response type the client wants. Don't redirect if those conditions are true. Return return a 401 status code instead. You can optionally return a JSON body with some extra error information.

Simple Login Auth React with Redux

I'm not sure what I'm missing but I have a simple login page that verifies a user.
This is my LoginPage.js function that handles the login.
import { connect } from "react-redux";
import { loginUser } from "../actions/auth";
...
<< Class declarations >>
...
handleLogin = e => {
e.preventDefault();
const creds = {
username: e.target.username.value,
password: e.target.password.value
};
console.log("Login data sent ", creds);
loginUser(creds);
};
...
<< Login Component rendered and form to handleLogin >>
...
export default connect(
undefined,
{ loginUser }
)(Login);
Which is sent to /actions/auth.js
export const loginUser = creds => {
console.log("login user creds ", creds);
return dispatch => {
console.log("inside dispatch ", creds);
try {
let response = API.post("api/login", {
username: "eve.holt#reqres.in",
password: "cityslicka"
});
console.log("Returned Data ", response);
dispatch(receiveLogin(creds));
return response;
} catch (e) {
console.log("Axios request failed ", e);
return false;
}
};
};
I have put console logs in to see where it goes but I only get:
Login data sent {username: "test", password: "test"}
login user creds {username: "test", password: "test"}
It doesn't seem to go any further than that so I don't see anything inside the dispatch.
Edit: I added that I am actually already using connect in the LoginPage.js
There are couple of updates we need to make in order for your login feature to work.
You're probably already somewhat familiar with the React-Redux flow.
A user interacts with your component, they trigger an event
(submitting a form/login).
We handle the event by calling our dispatcher function (the function we imported and plugged in connect()), taking the
user inputs to formulate a request to the back-end API. (redux-thunk action-creator)
We wait for the back-end API to verify the user credentials, if
successful they will give us back a token or user payload. (Promise-handler)
Use the returned object to dispatch an action, some info for our reducer to
handle. (Actual dispatch of action)
So let's try to create something to resemble that flow. To start, it looks like you've partially set-up an event-handler. The problem is that the loginUser() action-creator does not implicitly have access to the dispatch method. You need to call it from your props:
handleLogin = e => {
e.preventDefault();
const creds = {
username: e.target.username.value,
password: e.target.password.value
};
console.log("Login data sent ", creds);
this.props.loginUser(creds);
};
this.props.loginUser has dispatch binded to it thanks to the connect(), where as directly calling it from the import will not yield any redux functionality. This should be enough to complete 1 and 2.
Next we need to resolve number 3. At this point you should be able to execute the logic inside the dispatch function. However, the logic is not synchronous (ie, the flow of execution is not controlled), it does not wait for your API to give us something back before continuing.
You can introduce async/await for Promise-handling. In a nut-shell, it means to wait for something to complete before moving forward. Note that this will only work for promises. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
export const loginUser = creds => {
console.log("login user creds ", creds);
return async (dispatch) => {
console.log("inside dispatch ", creds);
try {
let response = await API.post("api/login", {
username: "eve.holt#reqres.in",
password: "cityslicka"
});
console.log("Returned Data ", response);
dispatch(receiveLogin(creds));
return response;
} catch (e) {
console.log("Axios request failed ", e);
return false;
}
};
};
For number 4, I will have to let you decide how to pass that data to your reducer :)
Your logic has a problem, I guess you want post request, if you get the correct response then dispatch. There is a function returned in your function. It doesn't make any sense to do this. Just execute it.
and use the dispatch function as a parameter is ok.

Shared method of store fields

Motivation
I store user credentials in redux store. They are filled when user logs in. I would like to have reusable method to fetch data with user's username and password.
State / auth
const initState = {
isLoading: false,
user: undefined,
auth_err: false
};
My attempt
const fetchData = props => async (url, method, body) => {
try {
const response = await fetch(url, {
method: method,
headers: {
'Content-Type': 'application/json',
'Authorization': 'Basic ' + Base64.btoa(props.user.username + ":" + props.user.password)
},
body: body
});
console.log(response);
return response;
} catch (err) {
console.log(err);
}
};
const mapStateToProps = state => {
return {
user: state.auth.user
}
};
export const SENDREQUEST = connect(mapStateToProps)(fetchData);
Call
const response = await SENDREQUEST("http://localhost:8080/users", "GET");
But once I call it I get:
TypeError: Cannot call a class as a function
Is there any way at all to create such one?
Any help would be appreciated ♥
I am assuming that you know about redux and its middleware.
First of all the error comes from passing fetchData to the return value of connect : connect returns a function which is a HOC : takes a component, returns a component which is a class here that cannot be called as a function as you do.
A solution for your problem is to use mapDispatchToProps and a middleware, roughly as follow :
class LoginFormPresenter {
render () {
// render your login
return <form onSubmit={this.props.logUser}></form>
}
}
// This makes the LoginFormPresenter always receive a props `logUser`
const LoginFormConnector = connect((state => { user: state.user }), {
logUser: (e) => (
// create a credentials object by reading the form
const credentials = ...;
// return a valid redux action
return {
type: "LOGIN",
credentials
};
)
});
const LoginForm = LoginFormConnector(LoginFormPresenter);
// Create an ad hoc middleware
//
const middleware = ({ dispatch, getState }) => next => action => {
if (action.type === "LOGIN") {
// log your user
fetch()
.then(user => next({ type: "LOGIN", user }));
return next({ type: "PROCESSING_LOGIN" }); // indicate to redux that you are processing the request
}
// let all others actions pass through
return next(action);
};
So the mechanism works like this:
The LoginFormConnector will inject a props logUser into any component it is applied to. This props is a function wich dispatches an action with the credentials of your user. It will also inject a user props taken from the redux state for you to show your user.
Inside a redux middleware you catch the action and use your generic fetchData to process the request. When the request is resolved you dispatch the action to the reducer and update it. No data fetching occurs in the component itself, everything is handled at the middleware level.

Categories

Resources