resolving race condition on API call - reactjs

I'm having a problem that seems to be due to an async call. I have an action that makes an API call and pushes to a Dashboard page. That API call also updates state.account.id based on the response it gives back:
const submitLogin = e => {
e.preventDefault();
props.loginAndGetAccount(credentials);
props.history.push('/protected');
e.target.reset();
}
loginAndGetAccount is coming from this action:
export const loginAndGetAccount = credentials => dispatch => {
dispatch({ type: GET_ACCOUNT_START })
axios
.post('https://foodtrucktrackr.herokuapp.com/api/auth/login/operators', credentials)
.then(res => {
console.log(res);
dispatch({ type: GET_ACCOUNT_SUCCESS, payload: res.data.id })
localStorage.setItem("token", res.data.token)
})
.catch(err => console.log(err));
}
On the Dashboard page, I have useEffect set up to make another API call dynamically based on the value held in state.account.id. However, it seems the first API call is pushing to the Dashboard page before the response comes back and updates state.account.id. Therefore, when the second API call is made there, it's passing state.account.id to that dynamic API call as undefined, which, of course, results in a failed call. How can I resolve this?
Here's what's happening:
const Dashboard = props => {
const [accountInfo, setAccountInfo] = useState({});
useEffect(() => {
console.log(props.accountId);
axiosWithAuth()
.get(`/operator/${props.accountId}`)
.then(res => {
console.log(res);
})
.catch(err => console.log(err));
}, [])
return (
<div>
<h1>This is the Dashboard component</h1>
</div>
)
}
const mapStateToProps = state => {
return {
accountId: state.account.id
}
}
export default connect(mapStateToProps, {})(Dashboard);

The root of the problem is that you are making a request here, but not
export const loginAndGetAccount = credentials => dispatch => {
dispatch({ type: GET_ACCOUNT_START })
axios
.post('https://foodtrucktrackr.herokuapp.com/api/auth/login/operators', credentials)
.then(res => {
console.log(res);
dispatch({ type: GET_ACCOUNT_SUCCESS, payload: res.data.id })
localStorage.setItem("token", res.data.token)
})
.catch(err => console.log(err));
}
waiting for it to complete here before you navigate to the next page
const submitLogin = e => {
e.preventDefault();
props.loginAndGetAccount(credentials);
props.history.push('/protected');
e.target.reset();
}
the quickest way to fix this is to returnt the promise from loginAndGetAccount and then props.history.push in the resolution of that promise...
like this:
export const loginAndGetAccount = credentials => dispatch => {
dispatch({ type: GET_ACCOUNT_START })
// return the promise here
return axios
.post('https://foodtrucktrackr.herokuapp.com/api/auth/login/operators', credentials)
.then(res => {
console.log(res);
dispatch({ type: GET_ACCOUNT_SUCCESS, payload: res.data.id })
localStorage.setItem("token", res.data.token)
})
.catch(err => console.log(err));
}
...
const submitLogin = e => {
e.preventDefault();
props.loginAndGetAccount(credentials)
.then(() => {
// so that you can push to history when it resolves (the request completes)
props.history.push('/protected');
e.target.reset();
}
.catch(e => {
// handle the error here with some hot logic
})
}

Related

run useEffect an other time when my function is called

I'm using useEffect to get all that I want from my backEnd,
When an onChange event is triggered, I call my function checkHandler method, where I make a post request to my backend. But the Get that I do first is not actualized, and the only way that I found to show the good stuff is to reload the page :s I think there is a better way to do it if my useEffect renders another time, but I don't know how to do it
const [message, setMessage] = useState([]);
useEffect(() => {
axios
.get(getAllMessage, config)
.then((res) => {
setMessage(res.data);
console.log("mounted");
})
.catch((err) => {
console.log(err);
});
}, []);
const checkHandler = (e) => {
let item = e.target.closest("[data-id]");
const disLikeMessage = `http://localhost:3001/api/like/dislike/${item.dataset.id}`;
const likeMessage = `http://localhost:3001/api/like/${item.dataset.id}`;
if (!item.checked) {
console.log("unchecked");
axios
.post(disLikeMessage, {}, config)
.then((res) => {})
.catch((err) => {
console.log(err);
});
} else {
console.log("checked");
axios
.post(likeMessage, {}, config)
.then((res) => {})
.catch((err) => {
console.log(err);
});
}
};
You don't need to refresh useEffect as there is no concept to refresh useEffect
Create a method that will fetch your all messages
const fetchMessages = () => {
axios
.get(getAllMessage, config)
.then((res) => {
setMessage(res.data);
console.log("mounted");
})
.catch((err) => {
console.log(err);
});
};
Inside your useEffect callback just call this fetchMessages()
useEffect(() => {
fetchMessages();
}, []);
Here is your checkHandler()
const checkHandler = (e) => {
let item = e.target.closest("[data-id]");
const disLikeMessage = `http://localhost:3001/api/like/dislike/${item.dataset.id}`;
const likeMessage = `http://localhost:3001/api/like/${item.dataset.id}`;
fetchMessages(); // Call fetchMessages wherever you need to fetch all messages
if (!item.checked) {
console.log("unchecked");
axios
.post(disLikeMessage, {}, config)
.then((res) => {})
.catch((err) => {
console.log(err);
});
} else {
console.log("checked");
axios
.post(likeMessage, {}, config)
.then((res) => {})
.catch((err) => {
console.log(err);
});
}
};

How do you save/post using axios correctly

I using Spring boot has backend and react-redux has frontend. The problem is where I try too save my data to my db the first click just save my first entity out of seven. After the second click it works normal and afterwards it works normal. I have try useEffect still the same problem.
export const setChecklist = (Checklist) => {return (dispatch) => {
console.log(Checklist);
axios
.post("http://localhost:8081/api/checklist/addList", Checklist)
.then((response) => {
console.log(response);
dispatch({
type: SET_CHECKLIST,
payload: response.data,
});
})
.catch((error) => {
console.log(error);
});
};
};
try this code:
export const setChecklist = async (Checklist) => {
const response = await axios
.post("http://localhost:8081/api/checklist/addList", Checklist)
.then((response) => {
console.log(response);
dispatch({
type: SET_CHECKLIST,
payload: response.data,
});
})
.catch((error) => {
console.log(error);
});
}
useEffect(() => {
setChecklist ()
.then((res) => {
setChecklist(res)
})
.catch((e) => {
console.log(e)
})
}, [])

I wanted to route to another page once redux dispatch will finish in react JS

const mapDispatchToProps = dispatch => ({
onQuestionLoad: payload => {dispatch({ type: FETCH_QUESTION_SET, payload });},
onLoad: payload => dispatch({ type: INSTRUCTION, payload }),
isShowTimer: payload => dispatch({ type: SHOW_TIMER, payload }),
setTestStatus: payload => {dispatch({ type: SET_TEST_STATUS, payload });},
onChange: payload => dispatch({ type: UPDATE_TIME, payload }),
resetTimer: payload => dispatch({ type: UPDATE_TIME, payload })
});
const startTest = () => {
Api.getTestStatus()
.then(res => {
if (res.data.message == "Success") {
let userTestId = res.data.data.userTestId;
Api.getTestStatusById(userTestId)
.then(r => {
if(r.data.message === 'Success') {
props.setTestStatus(res.data)
.then(()=>{
history.push('/testpanel');
})
}
})
}
})
.catch(error => {
setIsError(true)
})
}
I am using function-based component in ReactJS and Redux.
I wanted to execute the history.push('/testpanel') just after the finishing of props.setTestStatus(res.data)
You have to connect the testStatus state from your store to your component, and inside the render function you have to verify if testStatus is not null then redirect your user to /testpanel.
Note: don't use history.push('/testpanel') inside your reducer or action it's not good practice.

how to fix this axios call

I changed my code to handle API errors in one place and it stops working, can anyone identify what is the problem
before changing (working fine)
action.js
export const login = (email, password) => dispatch => {
axios
.post('http://localhost:8000/v1/users/signin/', {
email: email,
password: password,
})
.then(res => {
dispatch({
type: LOGIN_USER,
payload: res.data,
});
})
.catch(err => console.log(err));
};
after changing my code
action.js
import { postRequest } from '../services';
export const login = (email, password) => dispatch => {
postRequest('users/signin/', {
email: email,
password: password,
})
.then(res => {
dispatch({
type: LOGIN_USER,
payload: res.data,
});
})
.catch(err => console.log(err));
};
services.js
export const API_URL = 'localhost:8000/v1/';
export const postRequest = (request, body) => {
return axios.post(API_URL + request, body);
};
Did you forget 'http:' on API_URL?
export const API_URL = 'http://localhost:8000/v1/';

Reducer not called after action return?

Here's my login action code. What am I doing wrong ? As you can see, reducer state update not called.
Please, help me guys!
React - 16.8
Axios Http Client
Node & Mongo Db Backend
export const loginUser = (userData) => {
axios.post(URL + '/api/admin/login', userData)
.then(res => {
return {
type: SIGNIN_USER,
payload: storeData
}
})
.catch(err => {
return {
type: SHOW_MESSAGE,
payload: err.response.data
}
});
};
.then(res => {
return {
type: SIGNIN_USER,
payload: storeData
}
})
Instead of returning res, apply an action to it here. You mentioned changing the state, so something similar:
.then(res => {
this.state.someResult = res;
})
You need to dispatch the action, not just return the object:
const dispatch = useDispatch(); // Assuming you're inside functional component
export const loginUser = (userData) => {
axios.post(URL + '/api/admin/login', userData)
.then(res => {
return dispatch({
type: SIGNIN_USER,
payload: storeData
})
})
.catch(err => {
return dispatch({
type: SHOW_MESSAGE,
payload: err.response.data
})
});
};
Try with this code sample :
export const loginUser = userData => dispatch => (
axios.post(URL + '/api/admin/login', userData)
.then(res => dispatch({ type: SIGNIN_USER, payload: res }))
.catch(err => dispatch({ type: SHOW_MESSAGE, payload: err.response.data }))
)
Make use of Arrow functions it improves the readability of code. No need to return anything in API.fetchComments, Api call is asynchronous when the request is completed then will get the response, there you have to just dispatch type and data.
Below code does the same job by making use of Arrow functions.
export const bindComments = postId => {
return dispatch => {
API.fetchComments(postId).then(comments => {
dispatch({
type: BIND_COMMENTS,
comments,
postId
});
});
};
};
reference link : React-Redux: Actions must be plain objects. Use custom middleware for async actions

Resources