Redirect page if redux dispatch is success - reactjs

I only want to redirect if the post request is success on submit/onKeyDown using history.push(). The trouble is the function for my post request is in Redux Thunk.
While there is a way to create custom history and import history in redux and use history.push() in action/thunk I don't want to do that. I would like to keep history.push() in my React component. How can I do this?
My post function in my react component
const [patientName, setPatientName] = useState('');
const handlePatientNameChange = ({ target: { name, value } }) => {
setPatientName({ [name]: value });
};
const onKeyDown = (e) => {
if (e.key === "Enter") {
e.preventDefault();
dispatch(postRoomPatient(patientName, roomId));
// if dispatch is success history.push('/home')
}
};
// if error true - post is fail otherwise success.
const roomPatientError = useSelector((state) => state.roomPatients[roomId] && state.roomPatients[roomId].error);
my redux reducer/action
const roomPatientsSlice = createSlice({
name: 'roomPatients',
initialState: initialState,
reducers: {
setPatientSuccess: (state, action) => {
const { patientName, roomId } = action.payload;
const prevState = state[roomId];
state[roomId] = {
...prevState,
patientName: patientName,
error: false,
};
},
}
});
export const postRoomPatient = (patientName, roomId) => async dispatch => {
try {
const response = await axios.post('/patient/add', patientName, {
headers: { 'Content-Type': 'text/plain' }
});
const patientName = await response.data;
dispatch(setPatientSuccess({ patientName, roomId }));
// if success I want to history.push (`/home`) but do it in my React component
}
catch (err) {
console.log(err)
}
};

Related

Data is not stored in redux

I am trying to extend my frontend code with another redux call but the data is not appearing in store.
Here is store definition
const store = configureStore({
reducer: {
login: loginSlice.reducer,
cart: cartSlice.reducer,
product: productSlice.reducer,
notification: notificationSlice.reducer
}
});
Here is a slice
const productSlice = createSlice({
name: 'product',
initialState: {
products: []
},
reducers: {
replaceData(state,action) {
console.log(action)
state.products = action.payload.products;
}
}
});
export const productActions = productSlice.actions
export default productSlice
And action
export const fetchProducts = () => {
return async (dispatch) => {
const fetchHandler = async () => {
const resp = await fetch("https://shoppingcart-a62bb-default-rtdb.europe-west1.firebasedatabase.app/products.json")
const data = await resp.json();
}
try {
const productData = await fetchHandler();
dispatch(productActions.replaceData(productData))
} catch (err) {
dispatch(notificationActions.showNotification({
open: true,
message: "Error reading product data",
type: 'error'
}));
}
}
}
That's what I call in APP.js
useEffect(()=>{
dispatch(fetchCartData())
dispatch(fetchProducts())
},[dispatch]);
Here I read data from store in component
let respProducts = useSelector(state => state.product.products);
console.log(respProducts)
The problem is that fetch in action works,but payload in dispatch empty and no data in useSelector.
I really don't get what's wrong as similar code in the same app works.
Your fetchHandler is missing a return statement.
const fetchHandler = async () => {
const resp = await fetch("https://shoppingcart-a62bb-default-rtdb.europe-west1.firebasedatabase.app/products.json")
const data = await resp.json();
return data
}
use 'useReduxSelector' instead of 'useSelector'

React Redux Fast Update State

How to update the state just after dispatch?
State should be updated but is not.
What should be changed? Even when we will use then in our code -> even then we will not receive updated state, only when we will take value from the like .then((value) => { value.data }), but I want to take data from the state
Slice Code:
const authSlice = createSlice({
name: 'auth',
initialState: {
user: {},
},
reducers: {},
extraReducers: (builder) => {
builder
.addCase(activateUser.fulfilled, (state, action) => {
state.user = action.payload.userData
})
},
})
export const activateUser = createAsyncThunk('auth/activate', async (data) => {
try {
const userData = await authService.activateAccount(data)
return { userData: userData.data.data }
} catch (error) {
const message =
(error.response && error.response.data && error.response.data.message) ||
error.message ||
error.toString()
return message
}
})
const { reducer } = authSlice
export default reducer
Component:
function ActivateAccount() {
const { user } = useSelector((state) => state.auth)
const [code, setCode] = useState('')
const dispatch = useDispatch()
const activateUserAccount = () => {
const data = {
code: code?.value,
userId: user?._id,
email: user?.email || email?.value,
}
dispatch(activateUser(data))
console.log('Why here value is not updated yet?', user)
if (!user.activated) {
setCode({
...code,
isNotActivated: true,
})
return
}
return navigate('/on-board')
}
}
Why in the console log value is not yet updated?
What should be changed?
Any ideas?
Even though it's Redux it still needs to work within the confines of the React component lifecycle. In other words, the state needs to be updated and subscribers notified and React to rerender with the updated state. You are currently logging the user state closed over in function scope of the current render cycle. There's no possible way to log what the state will be on any subsequent render cycle.
You can chain from the asynchronous action though, and check any resolved values.
function ActivateAccount() {
const { user } = useSelector((state) => state.auth);
const [code, setCode] = useState('');
const dispatch = useDispatch();
const activateUserAccount = () => {
const data = {
code: code?.value,
userId: user?._id,
email: user?.email || email?.value,
}
dispatch(activateUser(data))
.unwrap()
.then(user => {
console.log(user);
if (!user.activated) {
setCode(code => ({
...code,
isNotActivated: true,
}));
return;
}
return navigate('/on-board');
});
}
...
}

Why my dispatch action doesn't work in use effect after request?

I need help. I don't understand why my dispatch action doesn't work. I've redux store currency list and current currency.
My reducer:
export const currencyReducer = (
state: typeState = initialState,
action: TypeActionCurrency
): typeState => {
switch (action.type) {
case types.CURRENCY_FILL_LIST:
return { ...state, list: action.payload }
case types.CURRENCY_SET_CURRENT:
return {
...state,
current:
state.list.find(currency => currency._id === action.payload) ||
({} as ICurrency),
}
default:
return state
}
}
My actions:
export const setCurrencyList = (currencies: ICurrency[]) => ({
type: types.CURRENCY_FILL_LIST,
payload: currencies,
})
export const setCurrentCurrency = (_id: string) => ({
type: types.CURRENCY_SET_CURRENT,
payload: _id,
})
My useEffect:
useEffect(() => {
if (!list.length) {
const fetchCurrencies = async () => {
try {
const data = await $apiClient<ICurrency[]>({ url: '/currencies' })
dispatch(setCurrencyList(data))
if (!current._id) dispatch(setCurrentCurrency(data[0]._id))
} catch (error) {
console.log(error)
}
}
fetchCurrencies()
}
}, [])
I want make request when load page and write currency list to Redux store, if we don't have current currency we write default currency from data.
There is one more strange thing, my redux extension shows that the state has changed, but when I receive it via the log or useSelector, it is empty
enter image description here
Thanks!
I am not 100% sure but it should work.
const [loader, setLoader] = useState(false);
const list = useSelector(state => state.list)
useEffect(() => {
if (!list.length) {
const fetchCurrencies = async () => {
try {
setLoader(true)
const data = await $apiClient<ICurrency[]>({ url: '/currencies' })
dispatch(setCurrencyList(data))
if (!current._id) dispatch(setCurrentCurrency(data[0]._id))
} catch (error) {
console.log(error)
} finally {
setLoader(false)
}
}
fetchCurrencies()
}
}, [])
useEffect(() => {
console.log(list);
}, [loader])

Why my action result data is null when I try console the state using redux and react js

Hi developers I am currently studying react js with redux for frontend and I want to implement state management (Redux) to my sample project. My Backend I use laravel. Now I already set the Action, Services, Reducers. When I try to console log the props state to my Component it shows that my action data response is null.
Problem: The action data response is null only.
Here is my MapState & mapDisPatch
const mapStateToProps = (state) => {
return {
filterChartRes: state.dashboard.filterChartRes,
}
}
const mapDisPatchToProps = (dispatch) => {
return {
loadFilterChartData: (selectionRange) => dispatch(loadFilterChartData(selectionRange)),
}
}
My Action:
export const loadFilterChartData = (selectionRange) => {
return (dispatch) => {
getFilterChartData(selectionRange).then((res) => {
console.log(res)
dispatch({ type: 'FILTER_CHART_RESPONSE', res })
},
error => {
dispatch({ type: 'FILTER_CHART_ERROR', error });
}
)
}
}
My Services:
export const getFilterChartData = (selectionRange) => {
const http = new HttpService();
//let filterData = selectionRange !== "" ? selectionRange : null;
let url = "auth/filter_chart";
return http.getData(url)
.then(data => {
return data;
})
}
My Reducers:
const initState = {
filterChartRes: null,
filterChartErr: null
};
const DashboardReducer = (state = initState, action) => {
switch (action.type) {
case 'FILTER_CHART_RESPONSE':
return {
...state,
filterChartRes: action.res.data
}
case 'FILTER_CHART_ERROR':
return {
...state,
filterChartErr: 'action.error'
}
default:
return state
}
}
export default DashboardReducer;
My Render:
const {filterChartRes } = this.props
console.log(filterChartRes, "My Filter");
My Work Output:
Back End Controller:
public function filter_chart() {
return 'Sample Data';
}
Hope Someone help on my problem
To solved this issue:
You must call the props inside the DidMount
componentDidMount = () => {
this.props.loadFilterChartData()
}

How can I access current redux state from useEffect?

I have a list of objects ("Albums" in my case) fetched from the database. I need to edit these objects.
In the editing component in the useEffect hook I fire up the action for getting the needed album using it's ID. This action works. However in the same useEffect I am trying to fetch the changed by before fired action redux state. And now I face the problem - all I am fetching is the previos state.
How can I implement in the useEffect fetching of current redux state?
I've seen similar questions here, however none of the answers were helpfull for my use case.
I am using redux-thunk.
Editing component. The problem appears in setFormData - it's fetching previous state from the reducer, not the current one. It seems that it fires before the state gets changed by the getAlbumById:
//imports
const EditAlbum = ({
album: { album, loading},
createAlbum,
getAlbumById,
history,
match
}) => {
const [formData, setFormData] = useState({
albumID: null,
albumName: ''
});
useEffect(() => {
getAlbumById(match.params.id);
setFormData({
albumID: loading || !album.albumID ? '' : album.albumID,
albumName: loading || !album.albumName ? '' : album.albumName
});
}, [getAlbumById, loading]);
const { albumName, albumID } = formData;
const onChange = e =>
setFormData({ ...formData, [e.target.name]: e.target.value });
const onSubmit = e => {
e.preventDefault();
createAlbum(formData, history, true);
};
return ( //code );
};
EditAlbum.propTypes = {
createAlbum: PropTypes.func.isRequired,
getAlbumById: PropTypes.func.isRequired,
album: PropTypes.object.isRequired
};
const mapStateToProps = state => ({
album: state.album
});
export default connect(
mapStateToProps,
{ createAlbum, getAlbumById }
)(withRouter(EditAlbum));
Action:
export const getAlbumById = albumID => async dispatch => {
try {
const res = await axios.get(`/api/album/${albumID}`);
dispatch({
type: GET_ALBUM,
payload: res.data
});
} catch (err) {
dispatch({
type: ALBUMS_ERROR,
payload: { msg: err.response.statusText, status: err.response.status }
});
}
};
reducer
const initialState = {
album: null,
albums: [],
loading: true,
error: {}
};
const album = (state = initialState, action) => {
const { type, payload } = action;
switch (type) {
case GET_ALBUM:
return {
...state,
album: payload,
loading: false
};
case ALBUMS_ERROR:
return {
...state,
error: payload,
loading: false
};
default:
return state;
}
};
Will be grateful for any help/ideas
You should split up your effects in 2, one to load album when album id changes from route:
const [formData, setFormData] = useState({
albumID: match.params.id,
albumName: '',
});
const { albumName, albumID } = formData;
// Only get album by id when id changed
useEffect(() => {
getAlbumById(albumID);
}, [albumID, getAlbumById]);
And one when data has arrived to set the formData state:
// Custom hook to check if component is mounted
// This needs to be imported in your component
// https://github.com/jmlweb/isMounted
const useIsMounted = () => {
const isMounted = useRef(false);
useEffect(() => {
isMounted.current = true;
return () => (isMounted.current = false);
}, []);
return isMounted;
};
// In your component check if it's mounted
// ...because you cannot set state on unmounted component
const isMounted = useIsMounted();
useEffect(() => {
// Only if loading is false and still mounted
if (loading === false && isMounted.current) {
const { albumID, albumName } = album;
setFormData({
albumID,
albumName,
});
}
}, [album, isMounted, loading]);
Your action should set loading to true when it starts getting an album:
export const getAlbumById = albumID => async dispatch => {
try {
// Here you should dispatch an action that would
// set loading to true
// dispatch({type:'LOAD_ALBUM'})
const res = await axios.get(`/api/album/${albumID}`);
dispatch({
type: GET_ALBUM,
payload: res.data
});
} catch (err) {
dispatch({
type: ALBUMS_ERROR,
payload: { msg: err.response.statusText, status: err.response.status }
});
}
};
Update detecting why useEffect is called when it should not:
Could you update the question with the output of this?
//only get album by id when id changed
useEffect(() => {
console.log('In the get data effect');
getAlbumById(albumID);
return () => {
console.log('Clean up get data effect');
if (albumID !== pref.current.albumID) {
console.log(
'XXXX album ID changed:',
pref.current.albumID,
albumID
);
}
if (getAlbumById !== pref.current.getAlbumById) {
console.log(
'XXX getAlbumById changed',
pref.current.getAlbumById,
getAlbumById
);
}
};
}, [albumID, getAlbumById]);

Resources