multiple dispatch in redux action - redux

I wanted to dispatch an action from another action but not able to do so. When I try to do so it not able to found getAllUser method.
Below is my action class.
export const myActions = {
getAllUser() {
return (dispatch) => {
makeApiCall()
.then((response) => {
dispatch({
type: USER_SUCCESS,
payload: response,
});
})
.catch((error) => {
dispatch({
type: USER_FAILURE,
payload: error,
});
});
};
},
addUser(user) {
return (dispatch) => {
makeApiCall(user)
.then((response) => {
/*
Need help here :
wants to call above getAllUser()
.then(() =>
dispatch({
type: ADD_SUCCESS,
payload: response,
});
)
*/
};
},
};
I have tried various approaches like,
myActions.getAllUser()
.then((response) =>
dispatch({
type: ADD_SUCCESS,
payload: response,
});
);
and trying do dispatch directly,
const self = this;
dispatch(self.getAllUser());
dispatch({
type: ADD_SUCCESS,
payload: response,
});
One more way around this is after addUser success, update the reducer and than from UI call getAccount again to refresh the results, but just curious to know on how can I achieve this using multiple dispatch.

You can export the functions individually instead of wrapping it under the same object:
export const getAllUser = () => dispatch => { ... }
export const addUser = () => dispatch => {
...
dispatch(getAllUser());
}
You can still import them all if desired:
import * as myActions from '...';
Or you can declare getAllUser first then add to myActions, but the above solution is much cleaner.
const getAllUser = ...
const myActions = {
getAllUser,
addUser = ... { dispatch(getAllUser()) }
}

Related

Dispatch in middleware leads to action with wrong type

I'm trying to create a simple middleware to handle socket events.
const join = (channel) => (dispatch) => {
dispatch({
type: 'ACTION-1',
socketChannel: {...},
events: [...],
});
};
I dispatch this action that triggers it. And now when the dispatch method was called in my middleware with type 'ACTION-2' and received socketData as a payload, I see in my console what 'ACTION-1' was triggered twice and in the last time it is came with my socketData payload.
I wonder why 'ACTION-1' was registered instead 'ACTION-2' and how I can fix it? I would appreciate your help.
import { socket } from 'services/socket';
const socketMiddleware = ({ dispatch }) => next => (action) => {
const {
channel,
events, // an array of events for the channel
...rest
} = action;
if (typeof action === 'function' || !channel) {
return next(action);
}
const {
type,
name,
} = channel;
const channelInstance = socket.instance[type](name);
events.forEach((event) => {
const handleEvent = (socketData) => {
dispatch({ type: 'ACTION-2', socketData, ...rest });
};
channelInstance.listen(event.name, handleEvent);
});
return next(action);
};
export {
socketMiddleware
};
looks like you are not pathing the channel in your initial dispatch and you are failing your middleware finishes inside this if:
if (typeof action === 'function' || !channel) {
return next(action);
}
in order to fix this you should add channel in your dispatch:
const join = (channel) => (dispatch) => {
dispatch({
type: 'ACTION-1',
socketChannel: {...},
events: [...],
channel: { type: '...', name: '...' }
});
};

unable to post values to api using axios in redux

I am trying to post data from a redux-form to an api server using axios.My action creator for posting the data looks like this:
//Action creator for submitting edited post
export function submitEditedPost(values, callback) {
const request = axios.post(`${API}/posts`, {headers}, {values});
return dispatch => {
return request.then((data) => {
callback();
console.log(data)
dispatch({
type:SUBMIT_POST,
payload: data
})
})
}
}
My onSubmit() methos of the form where the action-creator is called looks like this:
onSubmit(values) {
var id = Math.random().toString(36).substr(-8);
var d = new Date().toLocaleTimeString();
const formData = {};
for (const field in this.refs) {
formData[field] = this.refs[field].value;
}
formData.id = id;
formData.timestamp = d;
console.log('-->', formData);
this.props.submitEditedPost(formData, () => {
this.props.history.push('/');
});
}
When I try to console.log the edited values,I can see it correctly,but I am not able to post the edited values and update the api. The error message is shown in the screenshot below:
How do I proceed? Can someone please help me with this issue?
EDIT 1: My whole action file:
import axios from 'axios';
export const FETCH_POSTS = 'fetch_posts';
export const CREATE_POST = 'create_post';
export const FETCH_POST = 'fetch_post';
export const DELETE_POST ='delete_post';
export const EDIT_POST = 'edit_post';
export const SUBMIT_POST = 'submit_post';
let token ;
if(!token)
token = localStorage.token = Math.random().toString(36).substr(-8)
const API = 'http://localhost:3001';
const headers = {
'Accept' : 'application/json',
'Authorization' :'token'
}
//Action creator for fetching posts from the API server
export function fetchPosts() {
const URL = `${API}/posts`;
const request = axios.get(URL,{headers});
return dispatch => {
return request.then(({data}) => {
console.log(data);
dispatch({
type : FETCH_POSTS,
payload : data
})
})
}
}
//Action Creator for creating posts
export function createPosts(values, callback) {
return dispatch => {
return axios.post(`${API}/posts`,values,{headers})
.then((data) => {
callback();
console.log(data)
dispatch({
type: CREATE_POST,
payload: data
})
})
}
}
//Action Creator for displaying a selected post
export function fetchPost(id) {
const request = axios.get(`${API}/posts/${id}`,{headers});
return dispatch => {
return request.then(({data}) => {
console.log(data);
dispatch({
type: FETCH_POST,
payload: data
})
})
}
}
//Action creator for deleting post
export function deletePost(id, callback) {
const request = axios.delete(`${API}/posts/${id}`, {headers})
.then(() => callback());
return {
type: DELETE_POST,
payload: id
}
}
//Action creator for editing post
export function editPost(id, callback) {
const request = axios.get(`${API}/posts/${id}`,{headers});
return dispatch => {
return request.then((data) => {
callback();
console.log(data);
dispatch({
type: EDIT_POST,
payload: data
})
})
}
}
//Action creator for submitting edited post
export function submitEditedPost(id, values, callback) {
console.log(values, 'values')
console.log(id, 'id')
const request = axios.put(`${API}/posts/${id}`, {values}, {headers});
return dispatch => {
return request.then((res) => {
callback();
console.log("response", res)
dispatch({
type:SUBMIT_POST,
payload: res
})
})
}
}

How to refactor redux + thunk actions/constants

In my react/redux/thunk application I use actions like:
function catsRequested() {
return {
type: CATS_REQUESTED,
payload: {},
};
}
function catsReceived(landings) {
return {
type: CATS_RECEIVED,
payload: landings,
};
}
function catsFailed(error) {
return {
type: CATS_FAILED,
payload: { error },
};
}
export const fetchCats = () => ((dispatch, getState) => {
dispatch(catsRequested());
return catsAPI.loadCats()
.then((cats) => {
dispatch(catsReceived(cats));
}, (e) => {
dispatch(catsFailed(e.message));
});
});
To deal with some data (simplified). Everything works but i have a lot of code for every data entity (and constants too).
I mean same functions for dogs, tigers, birds etc...
I see there are similar requested/received/failed action/constant for every entity.
What is right way to minify code in terms of redux-thunk?
You can keep your code DRY by creating a types and a thunk creators:
Type:
const createTypes = (type) => ({
request: `${type}_REQUESTED`,
received: `${type}_RECEIVED`,
failed: `${type}_FAILED`,
});
Thunk:
const thunkCreator = (apiCall, callTypes) => ((dispatch, getState) => {
dispatch({ type: callTypes.request });
return apiCall
.then((payload) => {
dispatch({ type: callTypes.received, payload }));
}, (e) => {
dispatch({ type: callTypes.failed, payload: e.message }));
});
});
Now you can create a fetch method with 2 lines of code:
export const fetchCatsTypes = createTypes('CATS'); // create and export the constants
export const fetchCats = (catsAPI.loadCats, fetchCatsTypes); // create and export the thunk
export const fetchDogsTypes = createTypes('DOGS'); // create and export the constants
export const fetchDogs = (dogsAPI.loadDogs, fetchDogsTypes ); // create and export the thunk
Note: you'll also use the types constant (fetchDogsTypes) in the reducers.

setInterval props don't update

i dispatch action in the code below. And in the action i have loggingIn === false, but this.props.loggingIn === true.
How to fix it?
component.js
updateProfile() {
return { user: Meteor.user(), loggingIn: Meteor.loggingIn() }
}
componentWillMount() {
this.props.loadUser(this.updateProfile());
debugger;
this.data = setInterval(() => {
if(this.props.loggingIn === true) {
this.props.loadUser(this.updateProfile());
}
}, 1000);
}
actions.js
export const loadUser = ({user, loggingIn}) => dispatch => {
dispatch({
type: 'USER_DATA',
payload: user
})
dispatch({
type: 'USER_LOGGING_IN',
payload: loggingIn
});
};
You should be using redux-thunk for async calls. Here is the tutorial for using redux-thunk
and an example code for handling user authentication in Meteor

Action creators handling axios get.request with state access for param

I'm trying to pass some value from a component to a action creators which is doing a get request with axios. I'm trying to follow this pattern from Dan Abramov :
export const SOME_ACTION = 'SOME_ACTION';
export function someAction() {
return (dispatch, getState) => {
const {items} = getState().otherReducer;
dispatch(anotherAction(items));
}
}
However I can't make it work. I think I have trouble on two level : my component and my action creator. Would be great to have some helps.
my component :
const timeR = ({
selectedTimeRange,
timeRange = [],
onTimeChange }) => {
return (
<div>
<div>
Filters:
<div>
Year:
<select
defaultValue={selectedTimeRange}
onChange={onTimeChange}>
<option value="all" >All</option>
{timeRange.map((y, i) =>
<option key={i} value={y}>{y}</option>
)}
</select>
</div>
</div>
</div>
);
}
function mapStateToProps(state) {
var range = ['30daysAgo', '15daysAgo', '7daysAgo'];
return {
selectedTimeRange: state.timeReducer.timerange[0],
timeRange: range
};
};
const mapDispachToProps = (dispatch) => {
return {
onTimeChange: (e) => {dispatch (onSetTimeRange(e.target.value));},
};
};
const TimeRange = connect(mapStateToProps, mapDispachToProps)(timeR);
export default TimeRange;
This component give me a dropdown menu. When selecting a timerange, for example '30daysAgo', it update my redux store state so I can access the value from my reducer.
Here is the action associated to my dropdown menu :
export function onSetTimeRange(timerange) {
return {
type: 'SET_TIME_RANGE',
timerange
}
}
and here is the action dealing with axios.get :
export const fetchgadata = () => (dispatch) => {
dispatch({
type: 'FETCH_DATA_REQUEST',
isFetching:true,
error:null
});
var VIEW_ID = "ga:80820965";
return axios.get("http://localhost:3000/gadata", {
params: {
id: VIEW_ID
}
}).then(response => {
dispatch({
type: 'FETCH_DATA_SUCCESS',
isFetching: false,
data: response.data.rows.map( ([x, y]) => ({ x, y }) )
});
})
.catch(err => {
dispatch({
type: 'FETCH_DATA_FAILURE',
isFetching:false,
error:err
});
console.error("Failure: ", err);
});
};
My question :
How do I bring these two actions together. At the end I would like to be able, when doing onChange on my drop-down menu, to call a action with the value selected from my menu as a param for my axios.get request.
I feel like I need to nest two actions creators. I've tried this but doesn't work ("fetchgadata" is read-only error in my terminal)
export const SET_TIME_RANGE = 'SET_TIME_RANGE';
export function onSetTimeRange() {
return (dispatch, getState) => {
const {VIEW_ID} = getState().timerange;
dispatch(fetchgadata = (VIEW_ID) => (dispatch) => {
dispatch({
type: 'FETCH_DATA_REQUEST',
isFetching:true,
error:null,
id:VIEW_ID,
});
});
return axios.get("http://localhost:3000/gadata", {
params: {
id: VIEW_ID
}
}).then(response => {
dispatch({
type: 'FETCH_DATA_SUCCESS',
isFetching: false,
data: response.data.rows.map( ([x, y]) => ({ x, y }) )
});
})
.catch(err => {
dispatch({
ype: 'FETCH_DATA_FAILURE',
isFetching:false,
error:err
});
console.error("Failure: ", err);
});
}
}
Edit:
reducers for API call :
const initialState = {data:null,isFetching: false,error:null};
export const gaData = (state = initialState, action)=>{
switch (action.type) {
case 'FETCH_DATA_REQUEST':
case 'FETCH_DATA_FAILURE':
return { ...state, isFetching: action.isFetching, error: action.error };
case 'FETCH_DATA_SUCCESS':
return Object.assign({}, state, {data: action.data, isFetching: action.isFetching,
error: null });
default:return state;
}
};
reducers for Drop-down :
const items = [{timerange: '30daysAgo'},{timerange: '15daysAgo'},{timerange: '7daysAgo'}]
const timeReducer = (state = {
timerange: items
}, action) => {
switch (action.type) {
case 'SET_TIME_RANGE':
console.log(state,action);
return {
...state,
timerange: action.timerange,
};
default:
return state;
}
}
I see a little typo in the catch of your axios.get request, it reads ype: FETCH_DATA_FAILURE. Otherwise, can you add in your reducer for me, I don't see it up there? If I understand correctly, you want one action to update two different pieces of state, in which case you would simply dispatch an action and add it to both reducers. Really it's best to just demonstrate:
//axios call
axios.get("some route", { some params } )
.then(response => {
dispatch({
type: UPDATE_TWO_THINGS,
payload: some_value
})
}) .... catch, etc
//reducer 1
import { UPDATE_TWO_THINGS } from 'types';
const INITIAL_STATE = { userInfo: '' };
export default function (state = INITIAL_STATE, action) {
switch(action.type) {
case UPDATE_TWO_THINGS:
return {...state, userInfo: payload };
}
return state;
}
//reducer 2
import { UPDATE_TWO_THINGS } from 'types';
const INITIAL_STATE = { businessInfo: '' };
export default function (state = INITIAL_STATE, action) {
switch(action.type) {
case UPDATE_TWO_THINGS:
return {...state, businessInfo: payload };
}
return state;
}
Hopefully this helps, but let me know if not, I'll do my best to get this working with you! Thanks for asking!

Resources