Delete items from state array - redux - arrays

I'm working on react-native app with redux. I can't delete specific item from array. state.tournaments is array and item which i want to delete must contain ID which I'm sending from actions to redux.
This is my reducer:
import {
TOURNAMENT_NAME_CHANGED,
TOURNAMENT_CREATE,
SAVE_TOURNAMENT,
DELETE_TOURNAMENT
} from '../actions/types';
const INITIAL_STATE = {
name: null,
admin_token: null,
tournaments: []
};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case TOURNAMENT_NAME_CHANGED:
return { ...state, name: action.payload };
case TOURNAMENT_CREATE:
return { ...state, admin_token: action.payload.data.admin_token };
case SAVE_TOURNAMENT:
return { ...state, tournaments: [...state.tournaments, action.payload] };
case DELETE_TOURNAMENT:
return { ...state, tournaments: state.tournaments.filter((name, id) => id !== action.payload.id) };
default:
return state;
}
};

You're not using filter correctly, try this:
state.tournaments.filter(tournament => tournament.id !== action.payload.id)

Related

Defining a type for state and action Typescript

I am using state and action in a typescript file, which looks like this:
export const CartReducer = (state, action) => {
switch (action.type) {
case "ADD_ITEM":
if (!state.cartItems.find(item => item.id === action.payload.id)) {
state.cartItems.push({
...action.payload,
quantity: 1
})
}
return {
...state,
...sumItems(state.cartItems),
cartItems: [...state.cartItems]
}
case "REMOVE_ITEM":
return {
...state,
...sumItems(state.cartItems.filter(item => item.id !== action.payload.id)),
cartItems: [...state.cartItems.filter(item => item.id !== action.payload.id)]
}
case "INCREASE":
state.cartItems[state.cartItems.findIndex(item => item.id === action.payload.id)].quantity++
return {
...state,
...sumItems(state.cartItems),
cartItems: [...state.cartItems]
}
case "DECREASE":
state.cartItems[state.cartItems.findIndex(item => item.id === action.payload.id)].quantity--
return {
...state,
...sumItems(state.cartItems),
cartItems: [...state.cartItems]
}
case "CLEAR":
return {
cartItems: [],
...sumItems([]),
}
default:
return state
}
}
I am getting the following error:
src/contexts/CartReducer.tsx:28:30
TS7006: Parameter 'state' implicitly has an 'any' type.
26 | }
I am not sure of how to define type for State and Action, while I have declared types for other elements, I am not of the same for State and Action.
i am trying to follow this, for a typescript project.
You are following JS-only project, try following a typescript project.
interface MyState {
readonly cartItems: readonly any[];
...
}
type Reducer<T> = (currentState: T, action: { type: string, payload: any })
export const CartReducer: Reducer<MyState> = (state, action) => { ... }
Try to look for types for reducer/action for React. Better than reinvent the wheel.
I believe you need to define an InitialState
initialState = {
cartItems:[]
}
export const CartReducer = (state=initialState , action) => {
switch (action.type) {

React Redux how to add data to object's array

My user structure is:
user = {
email: 'email',
flashcards: []
}
And i would like to add data into user's flashcards array (using redux)
My user-reducer
import { UserActionTypes } from './user.types';
const INITIAL_STATE = {
currentUser: null,
};
// GETS STATES OBJ AND RECIVES AN ACTION
const userReducer = (state = INITIAL_STATE, action) => {
switch (action.type) {
case UserActionTypes.SET_CURRENT_USER:
return {
...state,
currentUser: action.payload,
};
case UserActionTypes.ADD_FLASHCARD:
return {
...state,
currentUser: action.payload,
};
default:
return state;
}
};
export default userReducer;
user-actions
export const addFlashCard = user => ({
type: UserActionTypes.ADD_FLASHCARD,
payload: user.flashcards,
});
And when i'm doing so my payload is undefined.
Could you give me some hints?
You are currently overwriting currentUser with the value of user.flashcards from the redux action. To add new flashcards, the ADD_FLASHCARD branch of your reducer should look more like this:
case UserActionTypes.ADD_FLASHCARD:
return {
...state,
currentUser: {
...state.currentUser,
flashcards: [
...state.currentUser.flashcards,
...action.payload
]
}
};

What are the pros and cons of using a single action to modify multiple values, or an action for each value? Why?

I'm trying to find what would be the best pattern to manage my reducers.
I have the following page:
I know I could've used redux-forms for this, but this is not the point since I only used these fields/form as an example.
We have multiple ways of handling this on redux:
1: having a single action to update those fields values based on the input's name property:
const UPDATE_VALUES = 'UPDATE_VALUES';
const INITIAL_STATE = {
aString: '',
setOfValues1: [],
setOfValues2: []
};
const reducer = (state = INITIAL_STATE, action) => {
switch (action.type) {
case UPDATE_VALUES: {
if (action.name === 'setOfValues1' || action.name === 'setOfValues2') {
const array = [...state[action.name]];
array.push(action.value);
return {
...state,
[action.name]: array
};
}
return {
...state,
[action.name]: action.value
};
}
default:
return state;
}
};
2: having multiple actions to each field value:
const UPDATE_A_STRING = 'UPDATE_A_STRING';
const UPDATE_SET_1 = 'UPDATE_SET_1';
const UPDATE_SET_2 = 'UPDATE_SET_2';
const INITIAL_STATE = {
aString: '',
setOfValues1: [],
setOfValues2: []
};
const reducer = (state = INITIAL_STATE, action) => {
switch (action.type) {
case UPDATE_A_STRING:
return {
...state,
aString: action.value
};
case UPDATE_SET_1: {
const array = [...state.setOfValues1];
array.push(action.value);
return {
...state,
setOfValues1: array
};
}
case UPDATE_SET_2: {
const array = [...state.setOfValues2];
array.push(action.value);
return {
...state,
setOfValues2: array
};
}
default:
return state;
}
};
and more ways that I'm not aware of.
what would be the good practice/best pattern in this case? Where can I look for, to learn more patterns to situations like these and other situations as well?
What about this?
const UPDATE_VALUES = 'UPDATE_VALUES';
const INITIAL_STATE = {
aString: '',
setOfValues1: [],
setOfValues2: []
};
const setOfValues = ['setOfValues1', 'setOfValues2'];
const reducer = (state = INITIAL_STATE, action) => {
switch (action.type) {
case UPDATE_VALUES: {
if (setOfValues.includes(action.name)) {
return {
...state,
[action.name]: state[action.name].concat(action.value);
};
}
return {
...state,
[action.name]: action.value
};
}
default:
return state;
}
};

Redux nested state

I have read many articles and examples, but I dont find a method to simplify a nested state. Is this possible to udpate a state without using a rest operator by property like the partial code below ?
const initialState = {
currentTag: 'fr-FR',
locales: {
components: [],
},
};
const setComponentsLocales = (state, payload) => ({
...state,
[payload.oid]: payload.locales,
});
const localesReducer = (state, action) => {
switch (action.type) {
case types.SET_COMPONENT_LOCALES:
return {
...state,
components: setComponentsLocales(state.components, action.payload),
};
default:
return state;
}
};
export default (state = initialState, action) => {
switch (action.type) {
case types.SET_LANGUAGE:
return {
...state,
currentTag: action.payload.tag,
};
default:
return {
...state,
locales: localesReducer(state.locales, action),
};
}
};

REACT can't do object assign in reducer

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
]

Resources