/reducers/index.js
import * as types from '../actions/ActionTypes';
var initialState = {
categoryName: 'default name redux'
};
function re_modifyCategoryName(state=initialState, action) {
switch(action.type) {
case types.MODIFY_CATEGORY_NAME :
return Object.assign({}, state, {
categoryName: action.categoryNewName
});
default:
return state;
}
}
export {re_modifyCategoryName}
I definitely check state of redux store is updated by Redux-dev-tool and redux-logger,
but mapStateToProps is not being called.
PresentationalComponent.js
...
const mapStateToProps = (state) => {
console.log("inside category name", state.categoryName);
return {categoryName: state.categoryName}
};
export default connect(mapStateToProps)(AnswerEachCategory);
...
When the page is rendered first time, as you can see mapStateToProps, "inside category name", state.categoryName is shown in console and presentational component correctly get props.categoryName from initialState of redux store. However, why the change in redux store didn't trigger mapStateToProps??
Is this problem related with immutability of state of redux store?
Container.js
...
const mapDispatchToProps = (dispatch) => ({
onModifyCategoryName: (categoryNewName) => {
dispatch(actions.modifyCategoryName(categoryNewName))
}
})
export default connect(null, mapDispatchToProps)(ModifyCategoryNameModal)
...
Related
the code below works fine, except something weird,
initial props is an empty object { photos: {} }
after invoking action creator the state in component looks like
{ photos: photos : {...} }
whereas I expect only photos: {...}
What cause this and how can I prevent generate another sub property in state?
component:
import {connect} from 'react-redux'
import {getPhotos} from '../actions'
class Photos extends Component {
constructor(props){
super(props)
}
componentDidMount(){
this.props.getPhotos();
}
invokeFunc= () =>{
this.props.getPhotos();
}
render() {
return (
<div>
<PhotoItem />
<button onClick={()=> this.invokeFunc()} >call action creator</button>
</div>
)
}
}
const mapStateToProps = (state) => {
const {photos} = state;
return {
photos,
}
}
export default connect(mapStateToProps,{getPhotos})(Photos)
action creator:
export const getPhotos = () =>{
return async (dispatch) => {
const response = await PhotosApi.get('/photos')
dispatch({
type:"GET_PHOTOS",
payload:response
})
}
}
reducer.js
const photosReducer = (state = {}, action) =>{
if(action.type=="GET_PHOTOS"){
return {...state, photos:action.payload}
}
return state;
}
export default combineReducers({
photos:photosReducer,
})
Your reducer is returning an object with photos as a property in it. Instead, try returning an object that is the photos property.
const photosReducer = (state = {}, action) =>{
switch(action.type){
case "GET_PHOTOS":
return { ...state, action.payload }; // or just `{ action.payload }` if you want it to wipe out the last state
default:
return state;
}
}
export default combineReducers({
photos:photosReducer,
});
The key here is combineReducers. From docs
The combineReducers helper function turns an object whose values are different reducing functions into a single reducing function you can pass to createStore.
The resulting reducer calls every child reducer, and gathers their results into a single state object. The state produced by combineReducers() namespaces the states of each reducer under their keys as passed to combineReducers()
So, since you are giving the namespace photos to your reducer, photosReducer state is under key photos in your global state.
I am trying to implement Redux on a React Hooks project, but it doesnt seems to work good. Am I doing something wrong here?
reducer.js
const initialState = {
educations: []
};
export default function home(state = initialState, action){
switch(action.type){
case GET_EDUCATIONS: {
state.educations = action.payload;
return state;
}
default:
return state;
}
}
action.js
import * as types from '../constans/home';
export const getEducations = () => {
return dispatch => {
const edus = [
{value: 1, name: 'Bachelor'},
{value: 2, name: "Master"}
]
dispatch({
type: types.GET_EDUCATIONS,
payload: edus
})
}
}
component
import React, {useEffect} from 'react';
import {connect} from 'react-redux';
import {getEducations} from '../../redux/actions/home';
function Header({educations, getEducations}) {
useEffect(() => {
getEducations(); //calling getEducations()
}, [])
useEffect(() => {
console.log(educations) //console educations after every change
})
return (
<div className="main-header">
</div>
)
}
const mapStateToProps = (state) => {
return {
educations: state.home.educations
}
}
const mapDispatchToProps = (dispatch) => {
return {
getEducations: () => { dispatch(getEducations())}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Header);
And the education property in Header function is always an empty array, as in initialState.
While when I check on browser with Redux Devtools, it shows that the state contains those two object in array.
So no matter if I change the redux state or not, the properties of the component are going to stay as initialState.
In redux, you should avoid directly mutating the state of your reducer. Refrain from doing something like state.reducers = blah. In order for redux to know that you are trying to make an update to state, you need to return a completely new state object. Following these principles, your reducers will update correctly and your components will get the new data.
Reducer.js
const initialState = {
educations: []
};
export default function home(state = initialState, action){
switch(action.type){
case GET_EDUCATIONS: {
return {
...state,
educations: action.payload
};
}
default:
return state;
}
}
In the code above, we return a new state object. It will include everything from the existing state, hence ...state, and we just update the educations property with the action.payload.
Can try with the reducer written this way :
const initialState = {
educations: []
};
export default function home(state = initialState, action){
switch(action.type){
case GET_EDUCATIONS:
return {
...state, educations:action.payload
}
default:
return state;
}
}
It looks like you’re mutating the state in the reducer. The reducer should always return a new state object if something updated.
You could do what the answers above suggest, but i would recommend using a package like immer (https://www.npmjs.com/package/immer) or immutable.js to prevent any bugs down the line. Using the spread syntax can be dangerous if your state object has some deeply nested properties, and it’s hard to be 100% sure that you haven’t accidentally mutated something, especially as your app grows in size.
It looks like you have solved this while I was getting this typed up - I decided to post it regardless, as it may be helpful.
On top of what Christopher Ngo already mentioned, the following example outlines how you can interact with your store to create new educations and then view them, in separate components..
Cheers!
I encounter this all the time and resolved it with CLEAR then GET/SET state. This ensures a reset of the state call.
Reducers.js
const initialState = {
educations: []
};
export default function home(state = initialState, action){
switch(action.type){
case GET_EDUCATIONS: {
return {
...state,
educations: action.payload
};
}
case CLEAR_EDUCATIONS: {
return initialState;
}
default:
return state;
}
}
Hooks.js
...
const clearEducation = () => {
dispatch({ type: CLEAR_EDUCATION });
}
const getEducations = (payload) => {
clearEducation(); // this clearing of the state is key fire re-render
dispatch({ type: GET_EDUCATIONS, payload });
};
}
Hi when i console log my components props (passed down from redux) i get the initial state which is null. however using the react inspector i have the result of the axios request. I tried reading dozens of similar problems but cannot seen to resolve my issue.
Actions
import { searchService } from '../api/searchService';
export const actions = {
FETCH_USERS: 'FETCH_USERS',
}
export const searchUsers = () => dispatch => {
searchService.get('/search')
.then((result) => {
dispatch({
type: actions.FETCH_USERS,
payload: result
})
})
}
Reducers
import { actions } from '../actions';
export default (state = null, action) => {
switch(action.type) {
case actions.FETCH_USERS:
return action.payload;
default:
return state;
}
}
Search Component
function mapStateToProps ({search}) {
return {search};
}
const mapDispatchToProps = dispatch => ({
searchUsers: () => dispatch(searchUsers())
});
export default connect(mapStateToProps, mapDispatchToProps)(withAuth()(Search));
Your problem is in the Reducer
First you should make an initial state, and then you need to edit this state in order for redux to feel the changes and update
Check the code below and let me know if it worked for you.
import { actions } from '../actions';
const INITIAL_STATE= {search: ""};
export default (state = INITIAL_STATE, action) => {
switch(action.type) {
case actions.FETCH_USERS:
return {...state, search: action.payload};
default:
return state;
}
}
I have created a basic reducer the state do get updated but render method does not get called here is the reducer file.
reducer.js
const accountSelector = (
store = {
items: [],
selectAll: false,
searchList: [],
filterList: [],
labelCount: 0
},
action
) => {
let newStore = {};
switch (action.type) {
case 'INITIALIZE_LIST':
console.log('in INITIALIZE_LIST');
newStore = { ...store, items: action.payload };
break;
case 'SEARCH_LIST':
newStore = { ...store, searchList: action.payload };
break;
case 'FILTER_LIST':
newStore = { ...store, filterList: action.payload };
break;
case 'SELECT_ALL_ACCOUNTS':
newStore = { ...store, selectAll: !store.list.selectAll };
break;
case 'UPDATE_LABEL_COUNT':
newStore = { ...store, labelCount: action.payload };
break;
default:
newStore = { ...store };
break;
}
console.log(' newStore: ', newStore);
return newStore;
};
export default accountSelector;
The state does get updated as I already logged it.
As you can see there are no mutation in reducer and still the render method does not get called.
Here are mapStateToProps and mapDispatchToProps
const mapStateToProps = state => ({
accountSelector: state.accountSelector
});
const mapDispatchToProps = dispatch => ({
initializeAccountsList: list => {
console.log('~~~~ ac selector ~~~~~~ in initializeAccountsList method');
dispatch({
type: 'INITIALIZE_LIST',
payload: list
});
}
Updated The question with store.js file where I combine reducer:
import { createStore, applyMiddleware, combineReducers } from 'redux';
import thunk from 'redux-thunk';
import promise from 'redux-promise-middleware';
import group from './reducer/groupReducer';
import clients from './reducer/clientReducer';
import accounts from './reducer/accountReducer';
import accountSelector from './reducer/accountSelectorReducer';
import createfeed from './reducer/createfeed';
const middleware = applyMiddleware(thunk, promise());
const reducers = combineReducers({
createfeed,
group,
clients,
accounts,
accountSelector
});
export default createStore(reducers, {}, middleware);
UPDATE: code for render method that has component that uses one of the state properties of accountSelector
render() {
let list = this.props.accountSelector.items;
if (this.props.accountSelector.searchList.length > 0) {
list = this.props.accountSelector.searchList;
}
return (
<div className="accountv2-select">
<UserList
updateLabelCount={this.updateLabelCount}
list={list}
selectedAccounts={this.props.selectedAccounts}
selectAll={this.props.selectAll}
selectedLoginIds={this.props.selectedLoginIds}
/>
);
}
Ofcourse the list will get the default value of items i.e is an empty array already defined in the reducer since the render method is not called even though state gets updated within reducer.
Removed old answer, as it's irrelevant now
It looks like your store.js file is combining reducers correctly, your mapStateToProps and mapDispatchToProps functions look fine, and your accountSelector reducer file looks fine, so at this point the only thing left is actually using your redux store data (from mapStateToProps) in your component.
You should be accessing state date (like items, searchList, filterList, ect.) like this:
// you mapped your entire accountSelector state to this.props.accountSelector
this.props.accountSelector.items
this.props.accountSelector.searchList
this.props.accountSelector.filterList
// ... etc
I'm developing React/Redux application and I've got problem with getting one particular state from redux store after dispatching an action. I don't have any idea why is that happening, because I haven't experienced such issue with other states. Here is my code:
Reducer
import {SET_CURRENT_USER, SET_LECTURES} from '../actions/actionTypes';
import isEmpty from 'lodash/isEmpty';
const initialState = {
isAuthenticated: false,
user: {},
lectures: []
}
export default (state = initialState, action = {}) => {
switch(action.type) {
case SET_CURRENT_USER:
return {
isAuthenticated: !isEmpty(action.user),
user: action.user
};
case SET_LECTURES:
return {
lectures: action.lectures
}
default: return state;
}
}
Action creator and dispatching action
import { SET_LECTURES } from './actionTypes';
export const setLectures = (lectures) => {
return {
type: SET_LECTURES,
lectures
}
}
export const lecture = (lectures) => {
return dispatch => {
console.log(lectures);
dispatch(setLectures(lectures));
}
}
The problem is with SET_LECTURES action type, in particular lectures property of action object. In the component from which I want to get state lectures, I do mapStateToProps as follows:
const mapStateToProps = function(state) {
return {
notifications: state.notifications,
lectures: state.lectures
}
}
/*
*Code of the class
*/
export default connect(mapStateToProps, null)(AddQuestionForm);
I've skipped code which triggers dispatching action type SET_LECTURES, because it's working fine. I've also used React Developer Tools for tracking states, and there is lectures state. I just can't get this state from my component, when I do console.log(this.props.lectures) from ComponentDidMount(), it shows undefined. Could you explain what am I doing wrong here? I would appreciate any help.
You forgot about dispatch:
export const lectureAction = lectures => dispatch => {
return dispatch => {
dispatch(setLectures(lectures));
}
}
In Component:
import { bindActionCreators } from 'redux';
const mapStateToProps = function(state) {
return {
notifications: state.notifications
}
}
// use map dispatch for actions:
const mapDispatchToProps = dispatch =>
bindActionCreators({
lectures: lectureAction
}, dispatch);
/*
*Code of the class
*/
// connect map dispatch here:
export default connect(mapStateToProps, mapDispatchToProps)(AddQuestionForm);
Now you have an access to this.props.lectures(someParams) function in your Component which dispatch an action.