Action firing, but reducer is not - javascript

Been struggling with this one for a while any help appreciated. With the below code it looks like the action fires successfully, but the reducer does not. No values are updated in redux store for rooms, but can confirm redux store is setup correctly and works fine for all my user actions and reducers.
I'm firing a function within a component getRoom('someId), passing along an id which then does a fetch and returns the result. Upon getting that result I'm dispatching a redux action saveRoom(res).
getRoom('someId')
.then(res => {
saveRoom(res);
})
.catch(e => console.log('Error', e));
Get room is just a fetch:
// Get room by id
export const getRoom = (roomId) => {
return fetch(`${API}/room/read/${roomId}`, {
method: "GET"
})
.then(response => {
return response.json();
})
.catch(err => console.log('Error', err))
};
Save room is a simple action. I can see it is being called and fired correctly in my console log.
import {SAVE_ROOM} from "./types";
export const saveRoom = (data) => {
console.log('Saving Room.', data);
return {
type: SAVE_ROOM,
payload: data
}
}
However, it doesn't seem like my reducer is firing at all. I have a console log to check but nothing returns in the console, and the redux store does not update.
import { SAVE_ROOM } from "../actions/types";
const initialState = { loading: true };
export default (state = initialState, action) => {
console.log('Save room reducer. Payload is: ', action.payload)
switch (action.type) {
case SAVE_ROOM:
return {
...state,
loading: false,
room: action.payload
};
default:
return state;
}
}
I have also ensured that the reducers are combined and can confirm they're present using redux tools
import { combineReducers } from "redux";
import userReducer from "./userReducer";
import roomReducer from "./roomReducer ";
export default combineReducers({
user: userReducer,
room: roomReducer
});

const dispatch = useDispatch()
and then
dispatch(saveRoom(res))

Related

How to filtered data with useSelector

Language used : JS with REACT REDUX
Here is the context: I have a page where the user can see all the forms made.
At the initialization of my application, I dispatch 'get forms'. this dispatch allows me via axios to retrieve all the forms I have in db
useEffect(() => {
dispatch(getForms());
}, []);
i have an action and reducer file :
actions
import axios from "axios";
export const GET_ALL_FORMS = "GET_ALL_FORMS";
export const getForms = () => (dispatch) =>
axios
.get(`/api/form`)
.then((res) => {
dispatch({ type: GET_ALL_FORMS, payload: res.data });
})
.catch((err) => err);
reducer
import { GET_ALL_FORMS } from "../actions/forms.actions";
const initialState = [];
export default function allFormsReducer(state = initialState, action) {
switch (action.type) {
case GET_ALL_FORMS:
return action.payload;
default:
return state;
}
}
Then in my "folder" page I use 'useSelector' to retrieve all the forms and display them (working).
import React, { useState, useEffect } from "react";
import { useSelector } from "react-redux";
export const FolderNew = () => {
const formData = useSelector((state) => state.allFormsReducer);
But now, at the click of a button, I would like to sorted my forms by date
Normally I don't use redux, so I would just do an axios request in my folder page and then use 'useState' (allForms, setAllForms) with my res.data and refresh the state like this :
const descend = () => {
const sorted = [...allForms].sort((a, b) =>
b.createdAt.localeCompare(a.createdAt)
);
setAllForms(sorted);
};
But now, with redux i'm really lost.
What i'm actually trying : Change the reducer to sort here the state (not working for the moment)
You can create a dispatch action something like sortForms(), then write the logic in your reducer:
export default function allFormsReducer(state = initialState, action) {
switch (action.type) {
case 'SORT_ALL_FORMS': {
const sortedForms = ....
return sortedForms
}
}
}
As Redux documents state, you should put as much logic as possible in your reducers:
https://redux.js.org/style-guide/#put-as-much-logic-as-possible-in-reducers

Redux action not firing - no errors

I'm trying to call a simple action on click which fires a dispatch action. I can't seem to get a result or even indiciation that it's firing.
I'm trying to dispatch on click in a component. I've also tried putting a console.log in the action to see if it even gets fired but it doesn't. Redux dev tools also doesn't suggest it even gets fired on click.
onClick={() => {
setAQIType(name);
}}
Action:
import { SET_AQITYPE } from "./types";
export const setAQIType = (AQIType) => dispatch => {
dispatch({
type: SET_AQITYPE,
payload: { AQIType }
});
};
Reducer:
import { SET_AQITYPE } from '../actions/types';
const initialState = {
aqiType: 'DEFAULT',
loading: false,
};
export default function(state = initialState, action){
const { type, payload } = action;
switch(type){
case SET_AQITYPE:
return [...state, payload];
default:
return state;
}
}
Types:
export const SET_AQITYPE = 'SET_AQITYPE';
Three errors,
In reducer: Your state is an object and not a list.
In reducer: Assign payload to aqiType key
In dispatch: Payload is a string and not an object.
To fix:
export const setAQIType = (AQIType) => dispatch => {
dispatch({
type: SET_AQITYPE,
payload: AQIType // (3) pass as string
});
};
// In reducer
case SET_AQITYPE:
return { // (1) object
...state,
aqiType: payload // (2) specify aqiType key
};
This assumes that you've checked the basic example with connect() and mapDispatchToProps.
Most likely you missed to connect the component with the redux store, which means there is no dispatch function passed to your action.
https://react-redux.js.org/using-react-redux/connect-mapdispatch
Cheers
Try to return inside action function as below:
import { SET_AQITYPE } from "./types";
export const setAQIType = (AQIType) => dispatch => {
return dispatch({
type: SET_AQITYPE,
payload: { AQIType }
});
};

React Redux API call, data not making it back to component

In the last couple of days I have been working on my Redux api call. I am actually having a problem getting the data back to the view component. Currently I'm able to see the data in the in the action generator, so I know at least I'm able to get it. However, nothing is showing in the view. I imagine it may have something to do with when it's loading. This is why I tried to load it when the component is rendering.
https://djangoandreact.herokuapp.com/user/1 is what is not loading.
codesandbox: https://codesandbox.io/s/zlor60q3jm?from-embed
Should be able to go to /user/1 at the end similar to going to /1 brings up an article(Tough Hope)
Heres the view component:
import React from "react";
import { connect } from "react-redux";
import { fetchUser } from "../store/actions/userActions";
class UserDetailView extends React.Component {
componentDidMount() {
const userID = this.props.match.params.userID;
fetchUser(userID); //fixed
}
render() {
const { user } = this.props.user;
console.log(user);
return (
<div>
<h3>{user.username}</h3>
</div>
);
}
}
const mapStateToProps = state => ({
user: state.user
});
const mapDispatchToProps = (dispatch, ownProps) => ({
fetchUser: dispatch(fetchUser(ownProps.match.params.userID))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(UserDetailView);
Action generator
import axios from "axios";
import { thunk } from "react-redux";
export function fetchUser(userID) {
console.log(userID);
return dispatch => {
return axios.get(`/api/user/${userID}`).then(res => {
dispatch(fetchUserSuccess(res.data));
console.log(res.data); // loads data
});
};
}
// Handle HTTP errors since fetch won't.
function handleErrors(response) {
if (!response.ok) {
throw Error(response.statusText);
}
return response;
}
export const FETCH_USER_BEGIN = "FETCH_USER_BEGIN";
export const FETCH_USER_SUCCESS = "FETCH_USER_SUCCESS";
export const FETCH_USER_FAILURE = "FETCH_USER_FAILURE";
export const fetchUserBegin = () => ({
type: FETCH_USER_BEGIN
});
export const fetchUserSuccess = user => ({
type: FETCH_USER_SUCCESS,
payload: { user }
});
export const fetchUserFailure = error => ({
type: FETCH_USER_FAILURE,
payload: { error }
});
Reducers(which are probably fine):
import {
FETCH_USER_BEGIN,
FETCH_USER_SUCCESS,
FETCH_USER_FAILURE
} from "../actions/actionTypes";
const initialState = {
user: {},
loading: false,
error: null
};
export default function userReducer(state = initialState, action) {
switch (action.type) {
case FETCH_USER_BEGIN:
return {
...state,
loading: true,
error: null
};
case FETCH_USER_SUCCESS:
return {
...state,
loading: false,
user: action.payload.user
};
case FETCH_USER_FAILURE:
return {
...state,
loading: false,
error: action.payload.error,
user: {}
};
default:
return state;
}
}
folks. I found it.
case FETCH_USER_SUCCESS:
return {
...state,
loading: false,
user: action.payload.user
};
user is supposed to be user:action.payload
Also, the user action was supposed to be
export const fetchUserSuccess = user => ({
type: FETCH_USER_SUCCESS,
payload: user
})
WOOOOW. But, honestly, I learned so much about Redux in the last two sleepless nights, it was worth the pain. Really was. Now, instead of copy pasta, I know what an action generator is and does, and reducer (obvi)

Reducer in react/redux app only working for one action.type

In my store.js i have the following code:
import { createStore, applyMiddleware} from 'redux';
import thunk from 'redux-thunk'
const reducer = (state, action) => {
console.log(action.type)
if (action.type === 'LOAD_USERS') {
return {
...state,
users: action.users['users']
}
} else if (action.type === 'LOAD_CHATROOMS') {
return {
...state,
chatRooms: action.chatRooms['chatRooms']
}
}
return state;
}
export default createStore(reducer, {users:[], chatRooms:[]}, applyMiddleware(thunk));
the code inside the action.type === 'LOAD_CHATROOMS' is never accessed for some reason, this is the action file where i set the action type for the reducer:
import axios from 'axios'
axios.defaults.withCredentials = true
const loadUsers = () => {
return dispatch => {
return axios.get('http://localhost:3000/session/new.json')
.then(response => {
dispatch({
type: 'LOAD_USERS',
users: response.data
});
});
};
};
const logIn = user => {
return axios.post('http://localhost:3000/session', {
user_id: user.id
})
.then(response => {
//TODO do something more relevant
console.log('loged in');
});
};
const loadChatRooms = () => {
return dispatch => {
return axios.get('http://localhost:3000/session/new.json')
.then(response => {
dispatch({
type: 'LOAD_CHATROOMS',
chatRooms: response.data
});
});
};
};
const enterChatRoom = chatrom => {
};
export { loadUsers, logIn, enterChatRoom, loadChatRooms};
The 'Load methods' get the data that i use to populate both components (one for users list and the other one for chatrooms list ), both components are called at the same level in the app.js file.
Basically the output that i'm getting is the first component (users) as expected with the correct list, and the chatrooms component is also rendered but the data is not loaded (since it's corresponding reducer block is not accessed).
Thanks a lot for reading :)

Accessing a part of reducer state from one reducer within another reducer

I do not know how to access a boolean isLoading flag from reducerForm.js reducer in reducerRegister.js. I have used combineReducers() and I use isLoading to disable a button during form submit.
It's initial state is false, after clicking submit, it changes to true. After the form submission is successful, isLoading is reset to false again. Below is the relevant code for this issue:
actionRegister.js
let _registerUserFailure = (payload) => {
return {
type: types.SAVE_USER_FAILURE,
payload
};
};
let _registerUserSuccess = (payload) => {
return {
type: types.SAVE_USER_SUCCESS,
payload,
is_Active: 0,
isLoading:true
};
};
let _hideNotification = (payload) => {
return {
type: types.HIDE_NOTIFICATION,
payload: ''
};
};
// asynchronous helpers
export function registerUser({ // use redux-thunk for asynchronous dispatch
timezone,
password,
passwordConfirmation,
email,
name
}) {
return dispatch => {
axios.all([axios.post('/auth/signup', {
timezone,
password,
passwordConfirmation,
email,
name,
is_Active: 0
})
// axios.post('/send', {email})
])
.then(axios.spread(res => {
dispatch(_registerUserSuccess(res.data.message));
dispatch(formReset());
setTimeout(() => {
dispatch(_hideNotification(res.data.message));
}, 10000);
}))
.catch(res => {
// BE validation and passport error message
dispatch(_registerUserFailure(res.data.message));
setTimeout(() => {
dispatch(_hideNotification(res.data.message));
}, 10000);
});
};
}
actionForm.js
export function formUpdate(name, value) {
return {
type: types.FORM_UPDATE_VALUE,
name, //shorthand from name:name introduced in ES2016
value
};
}
export function formReset() {
return {
type: types.FORM_RESET
};
}
reducerRegister.js
const INITIAL_STATE = {
error:{},
is_Active:false,
isLoading:false
};
const reducerSignup = (state = INITIAL_STATE , action) => {
switch(action.type) {
case types.SAVE_USER_SUCCESS:
return { ...state, is_Active:false, isLoading: true, error: { register: action.payload }};
case types.SAVE_USER_FAILURE:
return { ...state, error: { register: action.payload }};
case types.HIDE_NOTIFICATION:
return { ...state , error:{} };
}
return state;
};
export default reducerSignup;
reducerForm.js
const INITIAL_STATE = {
values: {}
};
const reducerUpdate = (state = INITIAL_STATE, action) => {
switch (action.type) {
case types.FORM_UPDATE_VALUE:
return Object.assign({}, state, {
values: Object.assign({}, state.values, {
[action.name]: action.value,
})
});
case types.FORM_RESET:
return INITIAL_STATE;
// here I need isLoading value from reducerRegister.js
}
return state;
};
export default reducerUpdate;
reducerCombined.js
import { combineReducers } from 'redux';
import reducerRegister from './reducerRegister';
import reducerLogin from './reducerLogin';
import reducerForm from './reducerForm';
const rootReducer = combineReducers({
signup:reducerRegister,
signin: reducerLogin,
form: reducerForm
});
export default rootReducer;
This is where I use isLoading:
let isLoading = this.props.isLoading;
<FormGroup>
<Col smOffset={4} sm={8}>
<Button type="submit" disabled={isLoading}
onClick={!isLoading ? isLoading : null}
>
{ isLoading ? 'Creating...' : 'Create New Account'}
</Button>
</Col>
</FormGroup>
Mapping state to props within the same component
function mapStateToProps(state) {
return {
errorMessage: state.signup.error,
isLoading: state.signup.isLoading,
values: state.form.values
};
}
This is covered in the Redux FAQ at https://redux.js.org/faq/reducers#how-do-i-share-state-between-two-reducers-do-i-have-to-use-combinereducers:
Many users later want to try to share data between two reducers, but find that combineReducers does not allow them to do so. There are several approaches that can be used:
If a reducer needs to know data from another slice of state, the state tree shape may need to be reorganized so that a single reducer is handling more of the data.
You may need to write some custom functions for handling some of these actions. This may require replacing combineReducers with your own top-level reducer function. You can also use a utility such as reduce-reducers to run combineReducers to handle most actions, but also run a more specialized reducer for specific actions that cross state slices.
Async action creators such as redux-thunk have access to the entire state through getState(). An action creator can retrieve additional data from the state and put it in an action, so that each reducer has enough information to update its own state slice.
A reducer cannot access another reducer's state, but if you're using redux-thunk you can do so from within an action creator. As an example, you can define an action creator like this:
export const someAction = () =>
(dispatch, getState) => {
const someVal = getState().someReducer.someVal;
dispatch({ type: types.SOME_ACTION, valFromOtherReducer: someVal });
};
React Redux works on unidirectional data flow.
Action ---> Reducer /store ---> Reducer
Reducer works on small subset of store, you can not access store inside reducer which is not part of Reducer. you can either need to fire new action from the component based on reducer state return.

Categories