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
Related
first of all i want to apologize for my title. I just dont know how to describe my problem.
I am trying to get a bad response from my server and when I try to display that my object is undefined
I have a base query methods here:
export const accountSlice = apiSlice.injectEndpoints({
endpoints: builder => ({
login: builder.mutation({
query: credentials => ({
url: 'account/login',
method: 'POST',
body: { ...credentials },
})
}),
register: builder.mutation({
query: credentials => ({
url: 'account/register',
method: 'POST',
body: { ...credentials },
})
})
})
})
My handle submit on register page ->
const [register, { isLoading, isError }] = useRegisterMutation();
const handleSubmit = async (e) => {
e.preventDefault();
try {
const result = await register({ name, nickName, email, password }).unwrap();
setRegisterResponse(result);
} catch (error) {
setRegisterResponse(error);
}
}
And my logic to show it. When i use console.log(registerResponse) it returnes two logs in console - first object is empty, second object with properties ->
{
isError &&
<h2>
Ooops.. something went wrong:
{
console.log(registerRespnse)
}
</h2>
}
Error in google console
You shouldn't need to call a setRegisterResponse state setter, because that response will just be available for you:
// see data and error here
const [register, { isLoading, isError, data, error }] = useRegisterMutation();
As why it logs undefined once: first the query finishes with an error (which will rerender the component and already fill error I showed above and set isError) and then the Promise resolves and your custom code sets your response local state, which causes a second rerender (and only on the second render, response is set)
I am using Promise and axios in react to call POST api and fetch list of records.
Issue is when multiple API calls triggered then any one which response last is getting is updating state.
Where i want to use only last called API response.
Exp : call API 3 times with different postbody, in case first call response take time than 2nd & 3rd then callback is using response of 1st call to setstate, instead i want to forget 1 and second call and consider last call response only.
Following is example
Common File
const apiService = axios.create({
baseURL: 'https://example.com/api/,
});
function post(postData) {
return Promise.resolve(apiService.post('https://example.com/api/getuserlist', postData, {
headers: {'Authorization': `Bearer sdfsdfsdf-cvdfs`}
}));
}
Service File
static getUsers(postData) {
return post(postData);
}
React Component
function getUsersList = (Filters )=>{
getUsers({ "Filters": Filters }).then((response) => {
this.setState({ users: response.data})
})
}
Problem is when getUsersList get called multiple time whichever is last response is getting set to users state, where last call should be users list.
It's not yet possible to actually cancel promises in JavaScript (though there is a proposal for it).
However, it is possible to implement the functionality you want in React. Consider something like this:
// state shape:
// {
// loading: boolean
// apiPromise: null | Promise<{ data: User[] }>
// users: User[]
// }
getUsersList = async (Filters) => {
// raw promise - don't await it yet
const apiPromise = getUsers({ Filters })
this.setState({ apiPromise, loading: true })
// _here_ we await it
const response = await apiPromise
// check if it's the most recent API request we made
if (this.state.apiPromise === apiPromise) {
this.setState({ users: response.data, loading: false })
} else {
console.log("stale val discarded:", users)
}
}
CodeSandbox demo - simplified example with mocked API and single val rather than list of users. Try clicking the button many times in quick succession and watch the console output.
Using CPromise wrapper the code might look like this See the live demo:
const CPromise = require('c-promise2');
const axios= require('axios');
// Let's wrap axios get method to the CPromise
function get(url){
return new CPromise((resolve, reject, {onCancel})=>{
axios.get(url, {
cancelToken: new axios.CancelToken(function executor(cancel) {
onCancel(cancel)
})
}).then(resolve, reject);
});
}
let chain= null;
function makeRequest(url){
chain && chain.cancel();
chain= get(url).then((response)=> {
console.log(`Response ${JSON.stringify(response.data)}`);
}, function (err) {
console.warn(`Error: ${err}`);
}
);
}
// some endpoint with a delay of 3 seconds for a response
const url= "https://run.mocky.io/v3/753aa609-65ae-4109-8f83-9cfe365290f0?mocky-delay=3s";
makeRequest(url);
// make the same request again, abort the previous
setTimeout(()=> makeRequest(url), 1000);
But since the package is in early beta stage, you can use the plain cancellation token, provided by axios See Axios documentation
I have a React application that uses loglevel-plugin-remote, as well as a custom API endpoint to receive the logs for error monitoring.
My log configuration is
import log from 'loglevel';
import remote from 'loglevel-plugin-remote';
const customJSON = log => ({
message: log.message,
level: log.level.label,
stacktrace: log.stacktrace
});
if (process.env.REACT_APP_SEND_LOGS === 'true') {
const apiPath = `${process.env.REACT_APP_API_PATH}/log`;
remote.apply(log, {
format: customJSON,
url: apiPath,
stacktrace: {
depth: 10
}
});
}
export default log;
And an example call is
import log from '../utils/logger';
...
const getUserData = () => {
return axios({url: userApi, method: 'GET', headers: authContext.authorizationHeaders })
.then(response => response.data)
.catch(error => {
log.error(error);
})
};
However, when the log gets called once, it will continually send requests to the API until I finally refresh the page.
The problem appeared to be with loglevel-plugin-remote which only checks to see if there's a 200 success status code response from the API, or else it will keep trying indefinitely.
My API, however, was returning a 201 message which I thought made more sense for creating a log entry. But when I changed the response to 200, I no longer had the issue. It appears to be an open issue with the repo, but hopefully, this helps others if you're seeing the same thing.
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.
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)
);