I have a Rest API that in order to get the end data AKA:ENDDATA I need to send few requests (depends how deep). So when sending the first request the return response will have in the body the nextUri and so on…
Basically I need to execute a list of Rest calls based on the nextUri till I reach to the end ENDDATA.
Is there a preferred way to do this with React & Redux\redux-thunk.
export const find = () => dispatch => {
dispatch({ type: FIND_CUSTOMER_START });
axios({
method: 'post',
url: '/v1/...',
headers: { 'X': 'XXX1' },
auth: {
username: 'XXX1',
password: 'XXX'
},
data: 'SELECT * FROM .....'
}).then(function (response) {
// Need to find the nextUri ... before update teh state
dispatch({
type: SUCCESS,
payload: response.data
});
}).catch(function (error) {
dispatch({ type: ERROR, payload: error });
});
}
Thank you
Define your api call in a redux action
Inside a component lifecycle method you can call this action. As well as map the state from the store to your component.
When the action updates the state it will cause a re-render and call the lifecycle method. In the lifecycle method you can check to see if ENDDATA is true and if not call the action again.
Inside render you can have the same conditional as in the lifecycle method and have it return a spinner to indicate loading.
Related
I'm working with a nice chatbot program for React someone wrote, and the thing is, you can actually bind the bot's responses to function calls like this:
render() {
const { classes } = this.props;
return (
<ChatBot
steps={[
{
...
{
id: '3',
message: ({ previousValue, steps }) => {
this.askAnswer(previousValue)
},
end: true,
},
]}
/>
);
Where message is the answer of the bot that it calculates based on the previousValue and askAnswer is a custom function you'd write. I'm using an API that inputs the previousValue to a GPT model, and I want to print the response of this API.
However, I just can't wrap my head around how I could pass the response of the API to the message. I'm trying this:
constructor(props) {
super(props);
this.state = { response: " " };
}
...
askAnswer(question) {
var jsonData = { "lastConversations": [question] }
fetch('http://my_ip:80/query', {
method: 'POST',
mode: 'cors',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(jsonData)
}).then(response => response.json())
.then(data => { this.setState({ response: data["botResponse"] }) });
return (this.state.response)
}
I've been struggling with this for the past 2-3 hours now.
I've tried a couple of combinations, and nothing seems to work:
If I do as seen above, it seems like this.state.response just won't get
updated (logging data["botResponse"] shows there is a correct
reply, so the API part works and I get the correct response).
If I try async-await for askAnswer and the fetch call, then I can
only return a Promise, which is then incompatible as input for the
ChatBot message.
If I do not await for the fetch to complete, then
this.state.response just stays the default " ".
If I return the correct data["botResponse"] from within the second .then after the fetch, nothing happens.
How am I supposed to get the API result JSON's data["botResponse"] text field out of the fetch scope so that I can pass it to message as a text? Or, how can I get a non-Promise string return after an await (AFAIK this is not possible)?
Thank you!
In your last 2 line
.then(data => { this.setState({ response: data["botResponse"] }) });
return (this.state.response)
You are updating the state and trying to read and return the value in the same function which I think will never work due to async behaviour of state. (this is why your this.state.response in the return statement is the previous value, not the updated state value).
I'm not sure but you can write a callback on this.setState and return from there, the callback will read the updated state value and you can return the new state.
i want to domething inside component after an action is done.
for example i want to show a modal to user after a request is successfully done, or disable some elements if request is done successfully.
should i use callbacks? or promise? if yes, then how
export const fetchHorizontalSpecialProductsList=(virtinId)=> {
return (dispatch) => {
dispatch({
type: Types.REQUEST_FETCH,
});
HomeApi().specialProducts({vitrinId:virtinId,rows:8,page:0,frontTypeList:["SPECIAL"]}).then((response) => {
dispatch({
type: Types.REQUEST_SUCCESS,
payload: response,
});
//
.then(probably here)
//
}).catch((response) => {
dispatch({
type: Types.REQUEST_FETCH_FAIL,
payload: response,
});
});
};
};
when the request is successful i need to show a modal
You just need to connect to Redux store, for example, in Redux state you should declare these properties:
success: false/true.
Then with the action received is REQUEST_FETCH_FAIL or REQUEST_SUCCESS you just need to use the switch..case statement in reducer to change the variable to true/false.
Your container connected to Redux store, depending on the props success true/false, you can doSomething() you would like to do,
I have an axios call inside my react application
}).then(res =>
this.setState({
name: res.data.fname,
sName: res.data.sname,
percentage: res.data.percentage,
result: res.data.result,
showResult: true,
type: this.typeHandler(res.data.percentage)
}));
}
after the request is done AND the state is set, I want to set input forms to blank. So i tried this
}).then(res =>
this.setState({
name: res.data.fname,
sName: res.data.sname,
percentage: res.data.percentage,
result: res.data.result,
showResult: true,
type: this.typeHandler(res.data.percentage)
})
).then(
this.setState(prevState =>{
return {inputName: '', inputSname: ''}
}))
}
using another promise, being resolved after the first, but the forms are still set to blank before the request state is being set. I have tried using a callback as well, but the first .then() method, does not accept a second argument. How can I solve this?
setState can get a call back
https://reactjs.org/docs/react-component.html#setstate
setState(updater[, callback])
instead of another then pass this callback to setState
I am new to redux-observable. I have an epic function which request an api call and emmit a new success action. What is the clean way to get the data in my react view? In my componentDidmount method I call the getUsers redux action. I wanted to set the state in componentDidmount but It will run only once and will not wait the promise epic.
const getUsersEpic = action$ =>
action$.ofType(GET_USERS)
.mergeMap( action =>
Observable.fromPromise(
axios({
method: 'get',
url: API_USERS,
headers : getHeaders().toObject()
})
)
)
.flatMap((response) => {
if(response.status == 200) {
return [{
type: GET_USERS_SUCCESS,
data: response.data,
status: response.status
}]
}
else {
return [{
type: GET_USERS_ERROR,
message: 'error',
status: response.status
}]
}
})
.catch( error => {
// console.log('error', error)
return setResponseError(error, GET_USERS_ERROR);
});
You dispatch an action in componentDidMount() that is intercepted by redux-observable and used to make ajax call. As a result, you get GET_USERS_SUCCESS or GET_USERS_ERROR action dispatched to redux at some point in the future. Generally, there is no way to await these actions in components. And I think it's a good limitation because this restricts async handling in the component which can in many ways introduce bugs.
What you need to do is set default state in reducer which is later updated by request response and pass it down as a props to your component.
Also, check out this answer by one of the redux-observable authors.
By the way is there any reason why you Axios instead AjaxObservable
Below an example what's goes wrong in my app that I can't figure out how to fix it and why it's actually happening.
I made a simple API call to fetch some data
dispatch(isLoadingData(true));
api({
method,
url,
headers: { Authorization: `JWT ${APP.token}` }
})
.then(async (response) => {
return resolve(dispatch(await updateData(response)));
}).catch(reject);
}).catch(async (err) => {
dispatch(isError(err));
dispatch(await isLoadingData(false));
throw err;
});
if API response has 401 status code that it initiates logout process by calling the next action.
case LOGOUT_USER: {
Actions.main({ type: 'reset' });
return {
...INITIAL_STATE,
};
}
that restores the user reducer to
{ user: '', token: '' };
(It calls before Promise returns a response in the upper function). So the sequence looks like this
1) isLoadingData(true)
2) logoutUser() (this clears user data)
3) isError(err) (this again has user and token)
4) isLoadingData(false)
As a result, the user can't be logging out.
just add another
.then(response => dispatchActionAfterActualLogoutWasMade())
so that you dispatch the action after the initial logout request was completed
that will send an action to reducer to clear the app state after you already received the response from the server.