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
Related
I have been working on a project where I am trying to update my selected data but Axios didn't Update it even after giving a success msg.
User Response it returns from axios:-
completed: true
date: "2021-02-28"
mupp_path: "PATH 1 - LIVING YOUR WHY - Build/Apply/Inspire/Spread (BAIS) – Finding & Achieving Meaning and Purpose in work and life"
project_name: "Design and Test the Training Content for the i-Infinity 3 verticals "
selected: true
task_id: 14
task_name: "This is adding a new task to chekc full inbox functionality "
task_type: "THIS_WEEK"
Actions.js
export const taskTodayUnselect = (id) => async (dispatch) => {
try {
dispatch({ type: types.UNSELECTED_TASK_TODAY_REQUEST });
const { data } = await axios.put(
selectTaskForToday,
{
task_id: id,
selected: false,
},
{
headers: {
Authorization: `JWT ${token}`,
},
}
);
if (data) {
return dispatch({ type: types.UNSELECTED_TASK_TODAY_SUCCESS, payload: data });
}
} catch (error) {
return dispatch({ type: types.UNSELECTED_TASK_TODAY_FAILURE, payload: error });
}
};
thisweek.js
export default function ThisWeek() {
const unselectTaskTodayAPI = (id) => {
dispatch(taskTodayUnselect(id)).then((response) => {
let result = response.payload;
console.log(result);
if (result.success === 'true') {
notifySuccess(result.message);
fetchTaskData(categoryID);
}
});
};
const selectTask = (item) => {
if (item.selected) {
unselectTaskTodayAPI(item);
console.log('unselect');
} else {
selectTaskTodayAPI(item.task_id);
}
};
return (
<TaskDataComponent
item={item}
key={item.task_id}
label="This Week"
selectTask={selectTask}
/>
);
Don't Worry about the TaskDataComponent , it only handle the onClick function which invoke the selectedTask function
I have React app which have page with Post. But now I rework this app to Redux.
And when in file Post.js I rework this small part:
useEffect(() => {
fetchListCategory();
}, []);
async function fetchListCategory() {
const dataCategories = await api(`${listRoute}?limit=100`, {
method: 'GET',
});
setValue(prev => ({
...prev,
listCategories: dataCategories.data,
}));
}
to Redux:
useEffect(() => {
fetchListCategory()(dispatch);
}, []);
function fetchListCategory() {
return async (dispatch) => {
dispatch({ type: "LOAD_DATA_START_SELECT" });
const dataCategories = await api(`${listRoute}?limit=100`, {
method: 'GET',
});
dispatch({ type: "LOAD_DATA_END_SELECT", payload: dataCategories });
}
I get error in file Post.js in line export default Post;:
'import' and 'export' may only appear at the top level
How to fix this error?
P.S.Now below in question I will write some part of file Post.js, and his children components. If you need more information write in the comments, I send for you screenshot or I add more information in question.
Post.js:
const Post = () => {
const dispatch = useDispatch();
const listImage = useSelector(state => state.filterImageReducer.listImage);
useEffect(() => {
fetchListCategory()(dispatch);
}, []);
function fetchListCategory() {
return async (dispatch) => {
dispatch({ type: "LOAD_DATA_START_SELECT" });
const dataCategories = await api(`${listRoute}?limit=100`, {
method: 'GET',
});
dispatch({ type: "LOAD_DATA_END_SELECT", payload: dataCategories });
}
useEffect(() => {
fetchDataPost()(dispatch);
}, []);
function fetchDataPost() {
return async (dispatch) => {
dispatch({ type: "LOAD_DATA_START_POSTS" });
const data = await apiImage(`${imageRoute}?orderBy=created_at`, {
method: 'GET',
});
dispatch({ type: "LOAD_DATA_END_POSTS", payload: data });
}
}
return (
<div>
<DisplayPost dataAttribute={listImage}/>
<AddPost fetchDataPost={fetchDataPost}/>
<EditPost fetchDataPost={fetchDataPost}/>
</div>
);
};
export default Post;
You have a missing } where fetchListCategory function is not properly terminated which is why you get this error.
function fetchListCategory() {
return async (dispatch) => {
dispatch({ type: "LOAD_DATA_START_SELECT" });
const dataCategories = await api(`${listRoute}?limit=100`, {
method: 'GET',
});
dispatch({ type: "LOAD_DATA_END_SELECT", payload: dataCategories });
}
} <- an extra bracket here
Also assuming you are using redux-thunk as a middleware, you do can simply dispatch fetchListCategory like
dispatch(fetchListCategory());
instead of
fetchListCategory()(dispatch);
Also you can take out the implementation of fetchListCategory outside of your component
I am using react+redux.
I have a modal form with data and images and on success I need to close the modal else display error returned from redux. In the dispatch function I have 1 more callback function to store images to S3. I am returning promise from the redux-thunk but I keep getting "TypeError: Cannot read property 'then' of undefined".
Component
handleSubmit = e => {
e.preventDefault();
if(this.isFieldEmpty()){
this.setState({ message: "All fields are mandatory with at least 1 pic" });
return;
} else {
this.setState({ message: "" });
}
const data = {
name: this.state.name,
description : this.state.description,
points : this.state.points,
attributes : this.state.attributes,
images : this.state.images,
created_by: localStorage.getItem('id'),
}
this.props.createItem(data).then(() => {
this.hideModal();
})
}
const mapDispatchToProps = dispatch => {
return {
createItem: data => {
return dispatch(createItem(data))
},
};
};
Action
const saveItemImages = (images,successcb, failurecb) => {
if(images.length > 0){
const formData = new FormData();
for(var x = 0; x<images.length; x++) {
formData.append('image', images[x])
}
const token = localStorage.getItem('token');
fetch(`${backendUrl}/upload/item-images/`, {
method: "POST",
headers: {
'Authorization': `Bearer ${token}`
},
credentials: 'include',
body: formData
})
.then(res => {
if(res.status === 200){
res.json().then(resData => {
successcb(resData.imagesUrl);
});
}else{
res.json().then(resData => {
failurecb(resData.message);
})
}
})
.catch(err => {
console.log(err);
});
} else {
successcb([]);
}
}
export const createItem = data => { return (dispatch) => {
saveItemImages(data.images, imagesUrl => {
data.images = imagesUrl;
return fetch(`${backendUrl}/admin/createItem`, {
method: 'POST',
headers: {
'Accept': 'application/json, text/plain, */*',
'Content-Type': 'application/json',
'Authorization': `Bearer ${data.token}`
},
credentials: 'include',
body: JSON.stringify(data)
})
.then(res => {
if(res.status === 200){
res.json().then(resData => {
dispatch({
type: ADMIN_CREATE_ITEM_SUCCESS,
payload: resData
})
return true;
});
}else{
console.log("Save failed");
res.json().then(resData => {
dispatch({
type: ADMIN_CREATE_ITEM_FAILED,
payload: {
message: resData.message
}
})
})
}
})
.catch(err => {
dispatch({
type: ADMIN_CREATE_ITEM_FAILED,
payload: {
message: `Internal Error -- ${err}`
}
})
});
}, failedMessage => {
let payload = {responseMessage: failedMessage}
dispatch({
type: ADMIN_CREATE_ITEM_FAILED,
payload: payload
})
});
};
};
Thanks in advance for any help
You should return a Promise to create async flow for the action like this:
export const createItem = data => dispatch => new Promise((resolve, reject) => {
// do something was a success
resolve();
// do something was a fail
reject();
});
I'm working with React Redux, and I'm trying to call a function inside another function on my actions file. The problem is It only work if I take the dispatch part of the function.
I wan't to call updateToken() from askForUser() but it will only work if I take the dispatch part from updateToken(). Also when i call askForUser() from askForUser() it won't work either.
const updateToken = (token) => dispatch => {
console.log(token)
dispatch({
type: UPDATE_TOKEN,
payload: token
})
}
const askForUser = (token) => dispatch => {
dispatch({ type: ASKING_FOR_DATA })
axios.get(API.GET_USER, {
params: {
token
}
}).then((response) => {
if (response.data.status === 1) {
dispatch({ type: ASK_FOR_USER, payload: response.data.user })
} else if (response.data.status === 2) {
updateToken(response.data.token)
//console.log(hola2())
} else {
NotificationManager.error('Error consiguiendo usuario')
dispatch({ type: ERROR_HANDLER })
}
}).catch((err) => {
console.log(err)
dispatch({ type: ERROR_HANDLER })
})
}
you'll have to dispatch it :
else if (response.data.status === 2) {
dispatch(updateToken(response.data.token)); // dispatch the function here
}
Had a look for this in the questions that offered but this was the closest and it didnt really address my problem.
I have a code block (detailed a little way down the page) as part of a larger fetch block.. it gets to this codeblock and also runs fine if this code block is commented out i.e it carrys out a successful fetch etc and returns a JWT no problem but... add this block in and i get the following error:
TypeError: (0 , _localStorageDropDowns.confirmSelectDataExistance)(...).then is not a function
It is referring to this function in another folder (imported correctly)..
export const confirmSelectDataExistance = () => {
const companyStateShortNameJson = localStorage.getItem(COMPANYSTATESHORTNAME)
const statesJson = localStorage.getItem(STATES)
const suburbLocationsJson = localStorage.getItem(LOCATIONS)
if (companyStateShortNameJson || statesJson || suburbLocationsJson) {
console.log('something exists in localstorage')
return true
}
console.log('nothing in localstorage')
return false
}
simple function - returns true or false.
and here is the code block -its failing on the first line:
return confirmSelectDataExistance().then(isConfirmed => {
if (!isConfirmed) {
dispatch({ type: REQUEST_SELECT_DATA })
console.log('gets here!', isConfirmed)
const token = getJwt()
const headers = new Headers({
'Authorization': `Bearer ${token}`
})
const retrieveSelectData = fetch('/api/SelectData/SelectData', {
method: 'GET',
headers: {
'Content-Type': 'application/json;charset=UTF-8'
},
})
.then(handleErrors)
.then(response => response.json())
.then(selectData => {
dispatch({ type: RECEIVE_SELECT_DATA, payload: selectData })
saveSelectData(selectData)
});
return saveSelectData(selectData);
}
})
From my limited experience the "confirmSelectDataExistance" is a function so why is it saying that its not?
Finally here is the whole action in its entirety so you can see how it that block is called.. as I said - comment the block out and it works perfectly..
export const requestLoginToken = (username, password) =>
(dispatch, getState) => {
dispatch({ type: REQUEST_LOGIN_TOKEN, payload: username })
const payload = {
userName: username,
password: password,
}
const task = fetch('/api/jwt', {
method: 'POST',
body: JSON.stringify(payload),
headers: {
'Content-Type': 'application/json;charset=UTF-8'
},
})
.then(handleErrors)
.then(response => response.json())
.then(data => {
dispatch({ type: RECEIVE_LOGIN_TOKEN, payload: data })
saveJwt(data)
return confirmSelectDataExistance().then(isConfirmed => {
if (!isConfirmed) {
dispatch({ type: REQUEST_SELECT_DATA })
console.log('gets here!', isConfirmed)
const token = getJwt()
const headers = new Headers({
'Authorization': `Bearer ${token}`
})
const retrieveSelectData = fetch('/api/SelectData/SelectData', {
method: 'GET',
headers: {
'Content-Type': 'application/json;charset=UTF-8'
},
})
.then(handleErrors)
.then(response => response.json())
.then(selectData => {
dispatch({ type: RECEIVE_SELECT_DATA, payload: selectData })
saveSelectData(selectData)
});
return saveSelectData(selectData);
}
})
})
.catch(error => {
clearJwt()
console.log('ERROR - LOGIN!',error)
})
addTask(task)
return task
}
EDIT
I have finally got this to work after hacking away for hours.. Here is the finished action:
export const requestLoginToken = (username, password) =>
(dispatch, getState) => {
dispatch({ type: REQUEST_LOGIN_TOKEN, payload: username })
const payload = {
userName: username,
password: password,
}
const task = fetch('/api/jwt', {
method: 'POST',
body: JSON.stringify(payload),
headers: {
'Content-Type': 'application/json;charset=UTF-8'
},
})
.then(handleErrors)
.then(response => response.json())
.then(data => {
dispatch({ type: RECEIVE_LOGIN_TOKEN, payload: data })
saveJwt(data)
// Now check local storage for dropdown data..
if (!confirmSelectDataExistance()) {
dispatch({ type: REQUEST_SELECT_DATA })
const token = JSON.stringify(data)
const headers = new Headers({
'Authorization': `Bearer ${token}`
})
const retrieveSelectData = fetch('/api/SelectData/SelectData', {
method: 'GET',
headers: {
'Content-Type': 'application/json;charset=UTF-8'
},
})
.then(handleErrors)
.then(response => response.json())
.then(selectData => {
dispatch({ type: RECEIVE_SELECT_DATA, payload: selectData })
saveSelectData(selectData)
});
}
})
.catch(error => {
clearJwt()
console.log('ERROR - LOGIN!', error)
})
addTask(task)
return task
}
and here is the function it calls:
export const confirmSelectDataExistance = () => {
const companyStateShortNameJson = localStorage.getItem(COMPANYSTATESHORTNAME)
const statesJson = localStorage.getItem(STATES)
const suburbLocationsJson = localStorage.getItem(LOCATIONS)
if (companyStateShortNameJson || statesJson || suburbLocationsJson) {
console.log('something exists in localstorage')
return true
}
console.log('nothing in localstorage')
return false
}
The one thing I changed from the other attempts is that I used "data" instead of calling "getJwt()". I then used the line:
const token = JSON.stringify(data)
to obtain the JWT I just got.
In the end I used #Karin s answer and ran with that. (upvoted by me)
The error is not saying that confirmSelectDataExistance is not a function, it's saying that then isn't a function on what is returned from it, which is a boolean (it would be equivalent to false.then(...), which doesn't work).
If seems like you're trying to use then as a conditional. In that case a simple if statement should work:
if (confirmSelectDataExistance()) {
// do things if it returns true
} else {
// do things if it returns false
}
export const confirmSelectDataExistance = () => {
return new Promise(function (resolve, reject) {
const companyStateShortNameJson = localStorage.getItem(COMPANYSTATESHORTNAME)
const statesJson = localStorage.getItem(STATES)
const suburbLocationsJson = localStorage.getItem(LOCATIONS)
if (companyStateShortNameJson || statesJson || suburbLocationsJson) {
console.log('something exists in localstorage')
resolve(true)
}
console.log('nothing in localstorage')
reject(false)
})
}
Try something like this:
export const confirmSelectDataExistance = new Promise((resolve, reject) => {
const companyStateShortNameJson = localStorage.getItem(COMPANYSTATESHORTNAME);
const statesJson = localStorage.getItem(STATES);
const suburbLocationsJson = localStorage.getItem(LOCATIONS);
if (companyStateShortNameJson || statesJson || suburbLocationsJson) {
console.log('something exists in localstorage');
resolve(true);
}
console.log('nothing in localstorage');
reject(false); // or resolve(false) if you want handle this situation inside then block also
});