Redux Thunk Submit to Action - reactjs

I been stuck to this quite a bit, I am trying to pass in my state to the redux but it seems like I am doing it wrong.
This are my code:
This is my submit function
popForm() {
let states = this.state.orders;
let d = states.filter((data) => {
return data !== null && data !== undefined
});
// console.log("d",d);
this.props.LogInClick(d);
// LogInClick(state);
}
This is my mapToDispatch
const mapDispatchToProps = (dispatch) => {
return {
LogInClick : (data) => dispatch(Actions.addDynamic(data)),
}
}
Action call
export const addDynamic = ({data}) => {
console.log("Manage to get to here");
console.log("dataInAction",data);
}
My reducer
case Actions.ADD_DYNAMIC: {
return {
...state,
data: action.payload
};
}

Your synchronous action should to return an object with type and payload.
When dealing with async actions, you need thunk(or saga etc) middleware. Your code seem to dispatch normal action (not async). So just make sure that your action returns type and payload.
Like this
export const addDynamic = ({data}) => {
console.log("Manage to get to here");
console.log("dataInAction",data);
return {
type: Action.ADD_DYNAMIC,
payload: data
}
}

Related

When the Next js app refreshing, useEffect not dispatching redux saga action and updating the state

My question is, when the next js app refreshing/reloading, redux store state not updating. I have the below code inside the component
const Landing = () => {
const freeADS = useSelector((state) => state.ads.freeAds); //this states are working fine without page refresh
useEffect(() => {
dispatch(fetchFreeAds());
}, [])
return(
{freeADS.map((data, i) => {
//some codings.........
})}
)
}
export default Landing;
redux action call
export const fetchFreeAds = () => {
return {
type: ActionTypes.FETCH_FREE_ADS
}
}
after the rootsaga / watch saga get the request, I call the handler like below
export function* handleFreeAds() {
const { response, error } = yield call(fetchFreeAds);
if (response)
{
yield put({type:"SET_FREE_ADS", payload: response.data[0]});
}
else{
}
}
actual api call goes here
export function fetchFreeAds() {
return axios.get('http://xxxxxxxxxx')
.then(response => ({ response }))
.catch(error => ({ error }))
}
I'm getting this error at the moment. pls give some support. thanks
Thanks to #slideshowp2
Problem solved by doing this miner modification. Added freeAds:[ ] backet to the initial state.
export interface State{
freeAds: null
}
export const adReducers = (state = {freeAds:[]}, {type, payload}) => {
switch(type)
case ActionTypes.SET_FREE_ADS:
return {
...state,
freeAds: payload
};
}

Block sending requests twice react-redux (hooks)

I have some hook for fetching countries array from api :
import { useSelector, useDispatch } from 'react-redux'
const useCountries = () => {
const dispatch = useDispatch()
const { countries } = useSelector(data => data)
useEffect(() => {
if (!countries.length) {
dispatch(getCountries())
}
}, [countries])
return {
countries
}
}
I have some components which aren't on the same level of DOM , and aren't related to each other
const FirstComponent = () => {
const {countries} = useCountries()
// some using of countries array
}
const SecondComponent = () => {
const {countries} = useCountries()
// some using of countries array
}
When page loading 2 of these components, are executed, and 2 actions are dispatched. Because countries aren't selected yet in first and in the second component it calls API request. My question is, how can I prevent sending the second request if some request was sent. Make some rule for sending request only once, if try to send the second one, then block it. Or what is the correct solution or approach in my case?
Also, I've tried a case like this :
const {
external: { response, isInitial }
} = useSelector(data => data)
useEffect(() => {
if (!response && isInitial) {
dispatch(setIsInitial(false))
fetch()
.then(res => res.json())
.then(data => {
setResponse(data)
})
}
}, [dispatch, response, isInitial])
Here is my reducer :
const initialState = {
response: null,
isInitial: true
}
export default function external(state = initialState, action) {
switch (action.type) {
case types.SET_RESPONSE:
return {
...state,
response: action.payload.data
}
case types.SET_INITIAL:
return {
...state,
isInitial: action.payload.data
}
default:
return state
}
}
But it sends 2 requests
You can modify your useCountries as below:
const useCountries = () => {
const dispatch = useDispatch()
const countries = useSelector(state => state.countries);
const isFetching = useSelector(state => state.isFetching);
useEffect(() => {
if (!countries.length && !isFetching) {
dispatch(getCountries())
}
}, [countries, isFetching])
return {
countries
}
}
You would need to flip the isFetching flag in your getCountries function and flip it again when the api has resolved.
------------------EDIT after additional information-----------------------
You would need to chain your dispatch calls. This is to ensure that flag is set before you make the api call.
To achieve that you can make dispatch thenable in the following way.
dispatch returns either of two:
For sync action (like dispatch ({type: 'ACTION'}) it will return
action object ({type: 'ACTION'} in my example)
For thunk actions (action creators which return functions) it returns
the same result returned from action creator.
This is the first case.
const syncAction = (arg1, arg2) => {
return (dispatch, getState) => {
return Promise.resolve(arg1 + arg2);
}
}
This would be the second case.
const asyncAction = (arg1, arg2) => {
return (dispatch, getState) => {
return fetch(/* some request */)
.then(response => dispatch({ type: "RESPONSE_RECEIVED", payload: response }));
};
};
Now armed with two above, you can do the following:
dispatch(syncAction(...args)).then(() => {
dispatch(asyncAction())
});
In your case syncAction - isInitial and asyncAction - getCountries().
Addind dispatch to useEffect dependencies array whould solve the issue.
const dispatch = useDispatch()
const { countries } = useSelector(data => data)
useEffect(() => {
if (!countries.length) {
dispatch(getCountries())
}
}, [countries, dispatch])

React Redux -possible to have a call back in dispatch function

Guys i am having some trouble or quite doubtful.
am having one component and one reducer.
Reducer.js
import {
ASSET_POPUP_GET_ENDPOINT,
} from 'apiCollection';
import { performGet } from 'services/rest-service/rest-service';
export const GET_ASSETS_LIST = 'stories/GET_ASSETS_LIST';
const initialState = {
imgGroup: [],
isLoading: false,
};
const modalUploadReducer = (state = initialState, action) => {
switch (action.type) {
case GET_ASSETS_LIST: {
return {
...state,
ImageJson:action.payload.imageGroup,
};
}
case GET_ASSETS_LIST_ERROR: {
return {
...state,
isLoading:false,
};
}
default:
return state;
}
};
export const getModalClose = () => (dispatch) => {
dispatch({ type: CLOSE_MODAL });
}
export const getListActionDispactcher = () => (dispatch) => {
performGet(`${ASSET_POPUP_GET_ENDPOINT}`)
.then((response) => {
const payload = response.data;
dispatch({ type: GET_ASSETS_LIST,
payload: {
...payload,
data: payload.results,
} });
})
.catch((err) => {
dispatch({ type: GET_ASSETS_LIST_ERROR, payload: err });
throw err;
});
};
export default modalUploadReducer;
and my component look like
it do have mapStateToProps and mapDispatchToProps
and one of the function
const mapDispatchToProps = dispatch => ({
getCollection: () => dispatch(getListActionDispactcher()),
});
addDocumentClick = () =>{
this.props.getAssetsCollection();
}
and is it possible to have some setState/manipulation of response after api response got from reducer in the component
based on the response i need to do some changes in addDocumentClick.
Means something like this
addDocumentClick = () =>{
this.props.getAssetsCollection().then(...based on response;
}
The correct way for solving this is setting a global loading flag and in your componentDidUpdate() method, checking for the value to determine that the action has just succeeded. You already seem to have the isLoading flag. Just set it when the action's dispatched, and unset it after it succeeds/fails. And in componentDidUpdate():
function componentDidUpdate(prevProps) {
if (prevProps.isLoading && !this.props.isLoading) {
// do something
}
}
Of course, you need to connect() your loading flag to your component to achieve this.
If all you care about is whether the assets list has changed, you can simply check for the change of that prop in componentDidUpdate():
function componentDidUpdate(prevProps) {
if (prevProps.ImageJson !== this.props.ImageJson) {
// do something
}
}
Another solution is sending a callback to your action dispatcher, which makes your code more tightly coupled and I don't recommend, but it does work too. So, when you connect(), you can:
getCollection: (onSuccess) => dispatch(getListActionDispactcher(onSuccess)),
In your action dispatcher:
export const getListActionDispactcher = (onSuccess) => (dispatch) => {
// ...once API finished/failed
onSuccess(someData);
}
Finally, in your component:
this.props.getCollection((result) => {
console.log('succeeded!', result);
// hide modal, etc..
}
You are using redux-thunk, and calling thunk will return a promise which will resolve in whatever you return in your thunk. Therefore, all you need to do is to add return value to getListActionDispactcher
export const getListActionDispactcher = () => (dispatch) => {
// return this promise
return performGet(`${ASSET_POPUP_GET_ENDPOINT}`)
.then((response) => {
const payload = response.data;
dispatch({ type: GET_ASSETS_LIST,
payload: {
...payload,
data: payload.results,
} });
// return whatever you want from promise
return payload
})
.catch((err) => {
dispatch({ type: GET_ASSETS_LIST_ERROR, payload: err });
throw err;
});
};
.
addDocumentClick = () => {
this.props.getAssetsCollection().then(payload => console.log(payload))
}
You should, however, look for ways to avoid this pattern to have your components decoupled from actions as much as possible for the sake of modularity

Promise is not getting returned in the redux thunk

I am new to react-redux. Here what I am doing is that,
I have an action that is like
export const updateActivePage = (activePage) => {
return (dispatch) => {
return dispatch ({
type: UPDATE_ACTIVEPAGE,
payload: activePage
});
}
}
Now, in my container, I want to call an action which will get called after this so,
handlePageChange = (pageNumber) => {
this.props.updateActivePage(pageNumber).then(() => {
this.props.fetchUserJd(this.props.activePage);
})
}
case UPDATE_ACTIVEPAGE: {
console.log("action payload", action.payload);
return {
...state,
activePage: action.payload
}
}
So, Here I am trying to use then but I am getting an error that Uncaught TypeError: _this.props.updateActivePage(...).then is not a function
So what is it that I am doing wrong?
You are fundamentally misusing redux-thunk. The intended use for this library is to dispatch an action creator (in this case, updateActivePage), which returns a function that gets invoked with state params. You should be dispatching updateActivePage since this is your thunk. Also, this is the function which should contain your computation, which should then posit the results into state. It appears that you are calling this function directly, and for some reason expect it to return a promise, and then somehow doing some computation. You should re-visit the thunk docs.
export const updateActivePage = (activePage) => {
return (dispatch) => {
return dispatch ({
type: UPDATE_ACTIVEPAGE,
payload: activePage
});
}
}
dispatch is not a promise, It just a function.
export const updateActivePage = (activePage) => {
return (dispatch) => {
return new Promise((res, rej) => {
dispatch ({
type: UPDATE_ACTIVEPAGE,
payload: activePage
});
res(true);
})
}
}
Can you try returning promise like I mentioned above?

Saving realtime listener in redux

I need to trigger firestore realtime listener on login to listen to user profile data changes and cancel it before logout. To do that I need to save realtime listener in the store where I get stuck. I'm trying to do this in redux
export const cancelListener = (cancelListener) => {
return {
type: actionTypes.CANCEL_LISTENER,
cancelListener: cancelListener
}
}
export const uDataListener = (uid) => {
return dispatch => {
dispatch(uDataStart())
const dbRef = db.collection("user").doc(uid)
const cancelSubscription = dbRef
.onSnapshot(
(doc) => {
dispatch(uDataSuccess(doc.data()))
}
, ((error) => {
dispatch(uDataFail(error.message))})
);
dispatch(cancelListener(cancelSubscription))
}
}
and on logout simply call it from the redux store
export const logout = (cancelListener) => {
cancelListener()
fire.auth().signOut()
return {
type: actionTypes.AUTH_LOGOUT
}
}
However nothing is being saved in cancelListener therefore it can not be triggered. How do I accomplish this task? Please
Thanks
I have woken up in the middle of the night with other idea. I tried to add the method in the constant in action instead of saving the method in the redux state or reducer. I'm not sure if this is the best approach but it does the job. Now I just don't understand why I didn't try this approach in the first place. Here is the code which will need a bit of tweaks yet but it works
let cancelListener = null
export const logout = () => {
cancelListener()
fire.auth().signOut()
return {
type: actionTypes.AUTH_LOGOUT
}
}
export const auth = (email, password) => {
return dispatch => {
dispatch(authStart())
fire.auth().signInWithEmailAndPassword(email, password).then((u) => {
dispatch(authSuccess(u.user))
const dbRef = db.collection("user").doc(u.user.uid)
cancelListener = dbRef.onSnapshot((doc) => {
dispatch(saveUserData(doc.data()))
})
}).catch((error) => {
dispatch(authFailed(error.message))
});
}
}
Thank you very much for your help anyway. I really appreciate that
Just a quick thought, in uDataListener call an action e.g. START_LISTENER and in reducer you can have:
import { store } from './yourStore';
let cancelListener, dbRef;
function reducer(state, action) {
switch (action.type) {
case "START_LISTENER":
dbRef = db.collection("user").doc(action.uid)
cancelSubscription = dbRef.onSnapshot(function(doc) {
store.dispatch(
yourAction(doc.data()); //Dispatch new action using store
)
})
return state;
case "STOP_LISTENER":
cancelListener()
return state;
default:
return state;
}
STOP_LISTENER will be dispached when you are doing logout
Below you can see link how to dispatch from outside a component
Update React component by dispatching action from non-react component

Resources