I have redux and react working correctly I believe and I have a child component firing off an action which then makes it as a new state to the parent component's mapStatetoDispatch method. However, while I can confirm through the console log that this state is indeed new, it never hits the render method (the props come back as undefined, just as they did before when there was no props to send down to them).
I must be missing something fundamental, I even tried all the component update or next state methods. Here is my component:
https://gist.github.com/geoffreysmith/ff909437a29ae8ab8db8038276b4f3b2
Thank you!
Edit: Here is my reducer:
import { combineReducers } from 'redux';
import { routerReducer as routing } from 'react-router-redux'
import { ADD_NEW_REPORT } from '../actions/Dashboard';
function reports(state = {}, action) {
switch (action.type) {
case ADD_NEW_REPORT:
return Object.assign({}, state, { [action.index]: addReport(undefined, action) })
default:
return state
}
}
function addReport(state, action) {
switch (action.type) {
case ADD_NEW_REPORT:
return {
index: action.index,
siteAddress: action.siteAddress,
receivedAt: action.receivedAt,
status: action.status
}
default:
return state
}
}
const rootReducer = combineReducers({
reports,
routing
})
export default rootReducer
I believe mapStateToProps should return an object (the props).
So I would change it to this:
const mapStateToProps = (state) => {
return {
reports: state.reports
}
}
Related
I have recently learned the MERN stack and is currently making a side project using it. I am stumbling into a problem in which my Redux Reducer is overriding each other everytime a different action is called. Here is the complete and detailed problem.
Questions:
Behaviour of invoking actions on a React Component. Let's say that we have decided to create an action and implement it in one of our React Component, we have done all of the setup (e.g. creating reducer and updating the state, creating the action itself and connecting it to the react component using connect from react-redux). I was just wondering when I call multiple actions in our React Component from componentDidMount, why does the state override each other. E.g.
componentDidMount(){
// Action updates 'user: {}' state in our reducer
this.props.fetchUserData();
// Action updates 'clients:{}' state
this.props.fetchClientsData();
}
Resulting State from Reducers:
user: {}, // Overrided by the fetchClientsData()
clients { clientsData }
Aren't we essentially update the specific reducer state (fetchUserData() updates 'user:{}' and fetchClientsData() updates 'clients: {}'). WHY IS IT OVERRIDING? I have checked my Redux Devtools middleware and It is infact overriding. HOW DO I NOT OVERRIDE THE OTHER STATE IN THE REDUCER? Thank you!
Update:
- Here is my reducers:
Index.js
import { combineReducers } from "redux";
import AuthReducer from "./AuthReducer";
import NotificationReducer from "./NotificationReducer";
import { reducer as formReducer } from "redux-form";
import DataReducer from "./DataReducer";
export default combineReducers({
form: formReducer,
auth: AuthReducer,
notification: NotificationReducer,
data: DataReducer,
});
AuthReducer.js
import { FETCH_USER, FETCH_HOSPITAL } from "../actionTypes";
export default (state = null, action) => {
switch (action.type) {
case FETCH_USER:
return action.payload || false;
case FETCH_HOSPITAL:
return action.payload || false;
default:
return false;
}
};
DataReducer.js
import { FETCH_DATA } from "../actionTypes";
export default (state = {}, action) => {
switch (action.type) {
case FETCH_DATA:
return { ...state, data: action.payload };
default:
return state;
}
};
The error lies in your AuthReducer, whatever you return from these switch case statement becomes your new state.
The same way you are doing in your DataReducer.js file
Therefore you have to update your state like:
export default (state = null, action) => {
switch (action.type) {
case FETCH_USER:
return {...state, users: action.payload || false};
case FETCH_HOSPITAL:
return {...state, clients: action.payload || false};
default:
return state;
}
};
Also it would be a good thing if you would initialize your state
I have setup a reducer in redux to to manage state. I am calling that reducer through an action creator. The action is dispatching (coz I hava subscribed to the store and put a console.log there)
This is what the reducer looks like
import C from "../constants"
import { combineReducers } from 'redux'
export const wishlist = (state = [], action) => {
switch (action.type) {
case C.REMOVE_PRODUCT_FROM_WISHLIST:
return state.filter(product => product.ProductName !== action.payload.ProductName)
default:
return state
}
}
export default combineReducers({
wishlist
})
Like I said, I have hooked it up with an action creator which I know is firing (again, coz the store is subscribing)
This is what the action creator looks like
import C from './constants'
export const removeProductFromWishlist = function(ProductName) {
return {
type: C.REMOVE_PRODUCT_FROM_WISHLIST,
payload: ProductName
}
}
Is there something I am missing here?
for reference, here is the initialState.json
{
"wishlist": [
],
}
Please let me know what am I doing wrong here?
I am learning how to implement redux from the ground up, and have run into a problem with my components' re-rendering. My search for this issue on StackOverflow has produced a gazillion results, of which the answer to the question is always "you mutated your state." I have read the connect documentation, I've looked at a bunch of people with similar problems, and I just can't see where state mutation might be the problem here, so I'm going to try asking with my simple example.
Here's my container component:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { addPokemon } from '../../../redux/actions';
import ReduxTester from '../../components/redux_tester/redux_tester';
export class ReduxTesting extends Component {
constructor(props) {
super(props);
}
addPokemon(name, pokeType) {
addPokemon(name, pokeType);
}
render() {
return (
<div>
<ReduxTester
pokemon={this.props.pokemon}
addPokemon={this.addPokemon}
/>
</div>
);
}
}
const MapStateToProps = function(state) {
return {
pokemon: state.pokemon,
};
};
export default connect(MapStateToProps)(ReduxTesting);
Here's my reducer:
const defaultState = {
pokemon: {},
};
export default function pokemonReducer(state = defaultState, action) {
const newState = Object.assign({}, state);
switch (action.type) {
case 'ADD_POKEMON':
newState.pokemon[action.name] = action.pokeType;
break;
default:
break;
}
return newState;
}
My specific issue is simply that ReactTesting's componentWillReceiveProps method is not firing, and so the component is not being updated and re-rendered. Note that the mapStateToProps method is firing after the action is dispatched. I know this is such a repetitive issue and it's unlikely that my problem is something different, but I just can't find the answer. Any assistance is appreciated.
Edit: For additional clarification, here is my actions.js, where I've imported the dispatch function directly:
import { dispatch } from './store';
// Returns an action that fits into the reducer
export function addPokemon(name, pokeType) {
dispatch({
type: 'ADD_POKEMON',
name,
pokeType,
});
}
Edit 2: I've found some additional information, but I don't understand it. It seems that in MapStateToProps, a re-render is triggered when I assign the entire Redux state to one prop - but not when I assign just a portion of the Redux state to prop.
This triggers a re-render:
const MapStateToProps = function(state) {
return {
pokemon: state,
};
};
This does not:
const MapStateToProps = function(state) {
return {
pokemon: state.pokemon,
};
};
Yet I have confirmed my redux store does have the pokemon property and that is where the updates are occurring in the state. Any insight?
Calling your action creator without going through redux won't dispatch the action:
export class ReduxTesting extends Component {
constructor(props) {
super(props);
}
addPokemon(name, pokeType) {
this.props.addPokemon(name, pokeType);
}
.
.
.
function mapDispatchToProps(dispatch) {
return({
addPokemon: (name, pokeType) => {dispatch(addPokemon(name, pokeType))}
})
}
export default connect(MapStateToProps, mapDispatchToProps)(ReduxTesting);
EDIT 2
You're probably mutating your state, try with something like this:
Reducer
switch (action.type) {
case 'ADD_POKEMON':
return {
...state,
pokemon[action.name]: action.pokeType;
};
I working on a react-redux webapp and until now, all action were related to ajax requests, so I'm using redux thunk and axios.
Now, I'm working in a component that would create a list from objects called 'tasks'. This should be very straight forward: the action calls send the task object and the reducer add it in an array inside the 'tasks' state.
The issue is that when called the reducer always rewrite the the tasks object array. Doing a console.log at the beginning of the reducer call I see the state is always empty.
Here is the code:
import {DROP_TASK} from '../actions/types'
export default function (state = {}, action) {
switch (action.type){
case(DROP_TASK):
console.log("state", state); // here is shows object{}
console.log('tasks', state.tasks);
if(state.tasks === undefined || state.tasks === null ) {
return {...state, list: [action.payload]}
}
return {...state, list: [...state.tasks.list, action.payload]};
default:
return state
}
}
at the createStore at the index.js
I'm doing:
const createStoreWithMiddleware = applyMiddleware(reduxThunk, logger)(createStore);
where logger just console.log the whole state before and after every dispatch. So I know that before the dispatch for the reducer function, the "tasks" is populated after a first call.
Here is how I'm combining reducers:
import { combineReducers } from 'redux';
import {reducer as form} from 'redux-form';
import {reducer as uiReducer} from 'redux-ui';
import authReducer from './auth_reducer';
import treeReducer from './tree_reducer';
import media_asset_list_reducers from './media_asset_list_reducers';
import tasks from './tasks';
const reducers = combineReducers({
form: form,
auth: authReducer,
tree: treeReducer,
media_asset_list: media_asset_list_reducers,
tasks: tasks,
ui: uiReducer
});
export default reducers;
Can anyone help me?
Thanks
You arenot showing how are you combining reducers. But it seems to me that half of your reducer is written to get substate in form tasks.list, but it returns new state in form just list.
If you compose reducers with combineReducers, each of the combined reducers gets just its subpart of state (and that is what it should return as well)
Try:
import {DROP_TASK} from '../actions/types'
export default function (state = {}, action) {
switch (action.type){
case(DROP_TASK):
return {...state, list: [...state.list, action.payload]};
default:
return state
}
}
Your reducer will receive the state.tasks slice of the state already, so there is no need to include .tasks in this reducer.
This will result in a state tree like:
{
... //other reducers
tasks: {
list: []
}
}
If you want to remove the list layer and just have tasks be a list you can change the reducer to:
import {DROP_TASK} from '../actions/types'
export default function (state = [], action) {
switch (action.type){
case(DROP_TASK):
return [...state, action.payload];
default:
return state
}
}
Also, DROP_TASK seems like an odd action type for adding an item to list... but not knowing more about your app, this may still be appropriate, so ignore this if so.
I have a Container, an actionsCreator, and a reducer. In the below code, what's enabling the Reducer to return action.text instead of an updated state object? I thought reducers had to always return states.
HelloWorldContainer.jsx
import { connect } from 'react-redux';
import HelloWorld from '../components/HelloWorld';
import * as actionCreators from '../actions/helloWorldActionCreators';
const mapStateToProps = (state) => ({ name: state.name });
export default connect(mapStateToProps, actionCreators)(HelloWorld);
helloWorldActionCreators.jsx
import { HELLO_WORLD_NAME_UPDATE } from '../constants/helloWorldConstants';
export const updateName = (text) => ({
type: HELLO_WORLD_NAME_UPDATE,
text,
});
helloWorldReducer.jsx
import { combineReducers } from 'redux';
import { HELLO_WORLD_NAME_UPDATE } from '../constants/helloWorldConstants';
const name = (state = '', action) => {
switch (action.type) {
case HELLO_WORLD_NAME_UPDATE:
return action.text
default:
return state;
}
};
const mainReducer = combineReducers({ name });
export default mainReducer;
(Code source: React on Rails).
The name is just a slice of state. And action.text is the updated state.
After combineReducers({ name }), the state tree looks like:
{
name: '..'
}
Besides, redux doesn't limit you that you can only use object as your state. If you pass name to createStore() directly without combineReducers, your entire state will become a plain string.
I thought reducers had to always return states.
No. Reducer has to always return data. Moreover, you should not return state, but a new object (or other data types).
So what is done in your case is that the reducer returns a new string (or whatever data type is text) every time HELLO_WORLD_NAME_UPDATE action is dispatched. It does not care what was already in the state and returns a new text string.