I know how to do with push method:
import * as types from '../constants/ActionTypes'
const initialState = {
designBox: [],
}
import * as types from '../constants/ActionTypes'
const initialState = {
designBox: [],
}
export default function(state = initialState, action) {
switch (action.type) {
case types.CREATE_DESIGN_BOX:
let newState = Object.assign({}, state);
newState.designBox.push(action.payload)
return newState
default:
return state
}
}
But I don't know how to do with ... method
Now my code has problem, The designBox can't add objects,
it only has one item, because it just overwritten by new action.payload
import * as types from '../constants/ActionTypes'
const initialState = {
designBox: [],
}
export default function(state = initialState, action) {
switch (action.type) {
// action.payload format -> { width:200,height:300,text:'abc'}
case types.CREATE_BOX:
return {
...state,
designBox: [action.payload]
}
default:
return state
}
}
How can I do this with ... method ??
Spread the the array as well:
return {
...state,
designBox: [...state.designBox, action.payload]
}
Also, your state doesn't need to be an object. If it only contains an array just make it an array:
const initialState = [];
Use array destructor
case types.CREATE_BOX:
return {
...state,
designBox: [...state.designBox,action.payload]
}
There's no way using object spread to manipulate keys, only replace them entirely. You can however refer to the original object when you're overriding keys:
return {
...state,
designBox: state.designBox.concat(action.payload)
}
Related
i've to clean up all my project from immutableJS, now my question is how can I replace
return state.set('loaded', true);
if i try to
state.loaded = true
ESlint show me this error Disallow Reassignment of Function Parameters (no-param-reassign
the complete code is this
import { Map } from 'immutable';
import { APP_READY } from '../actions/appActions';
import { USER_LOGOUT } from '../actions/logoutActions';
const initialState = Map({
loaded: false,
});
const appReducer = (state = initialState, action) => {
switch (action.type) {
case APP_READY:
return state.set('loaded', true);
case USER_LOGOUT:
return state.set('loaded', true);
default:
return state;
}
};
export default appReducer;
What's the best way to replace .set of Immutable js ?
Have you tried this:
const appReducer = (state = initialState, action) => {
switch (action.type) {
case APP_READY:
return {
...state,
loaded: true
};
default:
return state;
}
};
While you might get rid of ImmutableJS your resolver still must not mutate the state. You must return a new object. This can either be done in ES6 via { ...state, loading: true } or with Object.assign({}, state, { loaded: true }) if you are using an older version of EcmaScript.
I am trying to use stackoverflow api to make my first react redux project. I need to maintain a state like the following:
{
selectedTag: reactjs,
selectedSortOrder: activity,
items:[]
}
My reducer is given below:
const initialState = {
selectedTag: 'C#',
selectedSortOrder: 'activity', items: []
}
function SelectTag(state = initialState, action) {
switch (action.type) {
case SELECTTAG:
// console.log(state);
return Object.assign({}, state, { selectedTag: action.selectedTag });
default:
return state;
}
}
function SelectSortOrder(state = initialState, action) {
switch (action.type) {
case SELECTSORTORDER:
//console.log(state);
return Object.assign({}, state, { selectedSortOrder: action.selectedSortOrder });
default:
return state;
}
}
function ReceivePosts(state = { items: [] }, action) {
switch
(action.type) {
case RECEIVESORTEDPOSTS:
case RECEIVEPOST:
console.log(state);
return Object.assign({}, state, { items: action.items })
default:
return state
}
}
const rootReducer = combineReducers({ ReceivePosts, SelectTag, SelectSortOrder })
And mapStateToProps is:
const mapStateToProps = (state) => {
const selectedTag = state.SelectTag.selectedTag;
const items = (state.ReceivePosts.items);
const tags = (state.ReceiveTags.tags);
const selectedSortOrder = state.SelectSortOrder.selectedSortOrder;
return {selectedTag, items, tags, selectedSortOrder};
}
I have 2 problems here:
a. State does not remember all the data. For eg. suppose I select the tag first and then get items, my state has only items. SelectedTag is not set in the state.
b. I am not sure why mapStateToProps needs the reducer name. Eg: const selectedTag = state.SelectTag.selectedTag;
Actually it should be state.selectedTag. But my code expects the reducer name "SelectTag" to fetch the state value.
What am I doing wrong?
You haven't configured your reducers correctly. The initialState is assigned to all of your reducers which isn't required
const initialState={
selectedTag:'C#',
selectedSortOrder:'activity',
items:[]
}
function SelectTag(state = initialState.selectedTag, action){
switch(action.type){
case SELECTTAG:
return action.selectedTag
default:
return state;
}
}
function SelectSortOrder(state = initialState.selectedSortOrder, action){
switch(action.type){
case SELECTSORTORDER:
return action.selectedSortOrder
default:
return state;
}
}
function ReceivePosts(state = {items:[]}, action){
switch(action.type){
case RECEIVESORTEDPOSTS:
case RECEIVEPOST:
console.log(state);
return Object.assign({}, state, {items:action.items})
default:
return state
}
}
const rootReducer = combineReducers({ReceivePosts, SelectTag, SelectSortOrder})
And in mapStateToProps you would use it like
const mapStateToProps = (state) => {
const selectedTag = state.SelectTag;
const items = (state.ReceivePosts.items);
const tags = (state.ReceiveTags.tags);
const selectedSortOrder = state.SelectSortOrder;
return {selectedTag, items, tags, selectedSortOrder};
}
1. Try this code change
const initialState = {
selectedTag: 'C#',
selectedSortOrder: 'activity',
items: []
}
function SelectTag(state = initialState.selectedTag, action) {
switch (action.type) {
case SELECT TAG:
return {
...state,
selectedTag: action.selectedTag
}
default:
return state;
}
}
function SelectSortOrder(state = initialState.selectedSortOrder, action) {
switch (action.type) {
case SELECTSORTORDER:
return {
...state,
selectedSortOrder: action.selectedSortOrder
}
default:
return state;
}
}
function ReceivePosts(state = { items: [] }, action) {
switch (action.type) {
case RECEIVESORTEDPOSTS:
case RECEIVEPOST:
return {
...state,
items: action.items
}
default:
return state
}
}
const rootReducer = combineReducers({ ReceivePosts, SelectTag, SelectSortOrder });
2. I am not sure why mapStateToProps needs the reducer name. Eg: const selectedTag = state.SelectTag.selectedTag;
Its because when you use combinereducers, you are combining multiple slices of data, then you need to specify the slice from which you want to fetch the data.
const rootReducer = combineReducers({
receivePosts = ReceivePosts,
selectTag = SelectTag,
selectSortOrder = SelectSortOrder
});
Issue: You have not configured your initialstate properly, you are using the same initialstate in SelectTag and also in SelectSortOrder, if the initial state is same then why do you need two reducers?
I've got this reducer :
import { GET_CHAT, ADD_MESSAGE } from '../actions';
export default function (state = null, action) {
switch (action.type) {
case GET_CHAT:
return { ...state, [action.meta.id]: action.payload.data };
case ADD_MESSAGE:
console.log(state[action.meta.convId].chat.messages)
return {
...state,
[action.meta.convId]: {
...state[action.meta.convId],
chat: {
...state[action.meta.convId].chat,
messages: {
...state[action.meta.convId].chat.messages,
action.payload
}
}
}
};
default:
return state;
}
}
Where I want to add the action.payload to the end of the messages array. The issue is : when I try to put a key to the payload like so :
[action.meta._id]:action.payload
Messages is not an array anymore but becomes an object. So i can't loop over it anymore with map(). And redux won't let me push the payload directly to the array, it seems like i must put a key ...
Any help would be appreciated, thank's :)
If you want your messages to always be an array then your reducer should be like below:
import { GET_CHAT, ADD_MESSAGE } from '../actions';
export default function (state = null, action) {
switch (action.type) {
case GET_CHAT:
return { ...state, [action.meta.id]: action.payload.data };
case ADD_MESSAGE:
console.log(state[action.meta.convId].chat.messages)
return {
...state,
[action.meta.convId]: {
...state[action.meta.convId],
chat: {
...state[action.meta.convId].chat,
messages: [ //USE SQUARE BRACKETS INSTEAD
...state[action.meta.convId].chat.messages,
action.payload
]
}
}
};
default:
return state;
}
}
I have the following reducer below for user's submitting a rating and getting the user's ratings (your_ratings)...
When LOAD_YOURRATINGS_SUCCESS occurs, the two levels of initalState are being remove, and your_ratings is becoming the value of store.rating, when I want it to be store.rating.your_ratings.
What am I doing wrong below? Thanks
import * as types from '../actions/actionTypes';
const initialState = {
rating: {},
your_ratings: {}
}
export default function ratingReducer(state = initialState, action) {
switch (action.type) {
case types.CREATE_RATING_SUCCESS:
return action.rating
case types.LOAD_YOURRATINGS_SUCCESS:
return action.your_ratings
default:
return state;
}
}
You need to update your state and not overwrite it, you can do it with the help of spread operator like
export default function ratingReducer(state = initialState, action) {
switch (action.type) {
case types.CREATE_RATING_SUCCESS:
return {...state, rating: action.rating}
case types.LOAD_YOURRATINGS_SUCCESS:
return {...state, your_ratings: action.your_ratings}
default:
return state;
}
}
I got a problem when do objectAssign to change the state in store into a new data from server, It always get a null as the result.
i call my action in onEnter function(React-Router)
export function GET_SetupTabTitles() {
store.dispatch(getSetupTabTitles());
}
this is my action :
import {
TOGGLE_DRAWER_IN_APPBAR,
GET_SETUP_TAB_TITLES,
} from '../constants/actionTypes';
import axios from 'axios';
const ROOT_URL = 'http://localhost:8000';
export function toggleDrawerInAppBar(open){
return { type: TOGGLE_DRAWER_IN_APPBAR, openStatus: open }
}
export function getSetupTabTitles(){
return function(dispatch){
axios.get(`${ROOT_URL}/api/component/getSetupTabTitles`)
.then(response => {
dispatch({type: GET_SETUP_TAB_TITLES,
payload: response
});
});
}
}
this is my initial state on reducer :
export default {
auth: {
authenticated: (localStorage.getItem('laravel_user_token') !== null),
userinfo: {
name: null
},
error:""
},
comp: {
openDrawerStatus: false,
setupTabTitles: null,
}
};
and this is my reducer :
import {
TOGGLE_DRAWER_IN_APPBAR,
GET_SETUP_TAB_TITLES,
} from '../constants/actionTypes';
import initialState from './initialState';
import objectAssign from 'object-assign';
const compReducer = (state = initialState.comp, action) => {
switch (action.type) {
case TOGGLE_DRAWER_IN_APPBAR:
return objectAssign({}, state, {openDrawerStatus: action.openStatus});
case GET_SETUP_TAB_TITLES:
console.log(action.payload.data);
return objectAssign({}, state, {setupTabTitles: action.payload.data});
default:
return state;
}
};
export default compReducer;
when i do console.log inside
case GET_SETUP_TAB_TITLES:
it show :
Array[2]0: 0:Object 1:Object
On using JSON.stringify() it shows me [{"tabTitle":"Events"},{"tabTitle":"Tasks"}]
but my state (setupTabTitles) didn't change at all.
i do try this one :
case GET_SETUP_TAB_TITLES:
state.setupTabTitles.push(action.payload.data[0]);
return state;
it work, but i don't want to direct change the state.
You don't need to import ojectAssign from 'object-assign'; when you make use of the current ES6 syntax in your code. You only need Object.assign. Also since your action.data.payload is an array and you need to append to an array you can use the spread operator like
return {
...state,
setupTabTitles: [...state.setupTabTitles, action.payload.data]
}
Also you need to initialise you componentState to be an empty array and not null or undefined. Change that to below code
export default {
auth: {
authenticated: (localStorage.getItem('laravel_user_token') !== null),
userinfo: {
name: null
},
error:""
},
comp: {
openDrawerStatus: false,
setupTabTitles: [],
}
};
Try it like below
const compReducer = (state = initialState.comp, action) => {
switch (action.type) {
case TOGGLE_DRAWER_IN_APPBAR:
return Object.assign({}, state, {openDrawerStatus: action.openStatus});
case GET_SETUP_TAB_TITLES:
console.log(action.payload.data);
return {
...state,
setupTabTitles: [...state.setupTabTitles, ...action.payload.data]
}
default:
return state;
}
};
The syntax of objectAssign is different from what I use, you can see it here
var state = {
openDrawerStatus: false,
setupTabTitles: [],
}
var payload = [{"tabTitle":"Events"},{"tabTitle":"Tasks"}]
console.log( {
...state,
setupTabTitles: [...state.setupTabTitles, ...payload]
});
As you are already using ES6, you could just use the object spread operator and get rid of the object-assign library, it would be like this:
import {
TOGGLE_DRAWER_IN_APPBAR,
GET_SETUP_TAB_TITLES,
} from '../constants/actionTypes';
import initialState from './initialState';
const compReducer = (state = initialState.comp, action) => {
switch (action.type) {
case TOGGLE_DRAWER_IN_APPBAR:
return { ...state, openDrawerStatus: action.openStatus };
case GET_SETUP_TAB_TITLES:
return { ...state, setupTabTitles: action.payload.data };
default:
return state;
}
};
export default compReducer;
In your initial state, I would change setupTabTitle from null to an empty array []:
setupTabTitles: [],
And in your reducer, append data to this array:
const compReducer = (state = initialState.comp, action) => {
switch (action.type) {
...
case GET_SETUP_TAB_TITLES:
return {
...state,
setupTabTitles: [
...state.setupTabTitles,
...action.payload.data
]
}
...
}
};
Or if you don't want to append, just replace, I would do:
setupTabTitles: [
...action.payload.data
]