Duplicate items on redux state tree - react-native

I am trying to update my Redux state with a toggle button. My LB reducer is comprised of 2 reducers, an array called listItems for displaying a unique number of values, and filterBarState which is used as a reference to the current filters.
While my initial state is correct, my reducer places the toggle action outside of filterBarState
Below is my LB object reducer
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case EVENT_FILTER_PRESSED:
return {
// Reducer composition
filterBarState: eventFilter(state.filterBarState, action),
listItems: eventItems(state.listItems, action)
};
case MALE_FILTER_PRESSED:
// console.log('isMaleFilterOn:', action.isMaleFilterOn)
return { ...state, isMaleFilterOn: action.isMaleFilterOn };
case FEMALE_FILTER_PRESSED:
// console.log('isFemaleFilterOn:', action.isFemaleFilterOn)
return { ...state, isFemaleFilterOn: action.isFemaleFilterOn };
In my React Native Container Component, I have attempted some (suspect) ES6 destructuring in mapStateToProps which, if I don't include, the entire filterBarState returns undefined
const mapStateToProps = ({ LB }) => {
const { filterBarState: { isMaleFilterOn, isFemaleFilterOn, currentSelectedEvent, currentSelectedRow }, listItems, isCurrentlySelected } = LB;
return { isMaleFilterOn, isFemaleFilterOn, currentSelectedEvent, currentSelectedRow, listItems, isCurrentlySelected };
Any tips / suggestions on a fix would be greatly appreciated!

Based on the EVENT_FILTER_PRESSED handler, it looks like you are not updating the isMaleFilterOn and isFemaleFilterOn at the correct level. Try this:
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case EVENT_FILTER_PRESSED:
return {
// Reducer composition
filterBarState: eventFilter(state.filterBarState, action),
listItems: eventItems(state.listItems, action)
};
case MALE_FILTER_PRESSED:
// console.log('isMaleFilterOn:', action.isMaleFilterOn)
return { ...state, filterBarState: { ...state.filterBarState, isMaleFilterOn: action.isMaleFilterOn } };
case FEMALE_FILTER_PRESSED:
// console.log('isFemaleFilterOn:', action.isFemaleFilterOn)
return { ...state, filterBarState: { ...state.filterBarState, isFemaleFilterOn: action.isFemaleFilterOn } };
}

Related

Reducer gives an invalid attempt spread non-iterable instance

Would like to know whats wrong with my code it gives an error when i try dispatching a action using redux this is my reducer.js code
import {UPDATE_LOGIN} from './constants'
const initialState = {
isLogged:false
};
const reducer = (state = initialState, action) => {
switch (action.type) {
case UPDATE_LOGIN:
return [
...state,
{
isLogged:action.isLogged
}
];
default:
return state;
}
}
export default reducer;
it gives a Invalid attempt to spread non-iterable instance.
In order to be iterable, non-array objects must have a Symbol.iterator method.
Your state is an object, and you try to destructure it as an array. In addition, you try to include the object, instead of changing the property. Try this:
const reducer = (state = initialState, action) => {
switch (action.type) {
case UPDATE_LOGIN:
return {
...state,
isLogged: action.isLogged
};
default:
return state;
}
}

mapStateToProps called but not calling componentWillRecieveProps when state has same value

I have the following reducer:
export default function UserReducer(state = initialState, action){
let nextState;
switch(action.type){
case USER_RECOVERY_CODE_VALIDATED:
nextState = {
...state,
recoverycodevalidated : action.payload.recoverycodevalidated
}
return nextState;
default:
return state;
}
}
And my component is connected to the store that way:
function mapStateToProps(state) {
console.log("updated");
return { recoverycodevalidated: state.user.recoverycodevalidated };
}
export default connect(mapStateToProps)(ResetPasswordCodeScreen)
And when the props change thanks to the mapStateToProps function, I redirect the user to the next screen:
async componentWillReceiveProps(newProps){
console.log("updated props");
if(newProps.recoverycodevalidated){
this.props.navigation.navigate("ResetPasswordFinal", { userId: this.props.navigation.state.params.userId});
}
}
The problem is that when the state is updated the first time and the value of recoverycodevalidated is set in the props, the second time if the value of the variable is the same, the props are not updated because the method componentWillRecieveProps is not fired, eventhough the mapStateToProps is fired everytime. What I'm doing wrong ?
Try using componentDidUpdate instead
componentDidUpdate(prevProps) {
console.log("updated props");
if (prevProps.recoverycodevalidated) {
this.props.navigation.navigate("ResetPasswordFinal", { userId:
this.props.navigation.state.params.userId});
}
}

Updating a reducer from another reducer in react-native

I want to update the state of one reducer from another reducer in react-native.My code is like this.
This is the action.
export const doLike = payload => {
// Recieving response from the server
let updatedPost = {
Id: 1,
userId: 1,
postId: payload.post._id,
__v: 1,
count: payload.type === 1 ? 10 : 1
};
return {
type: SM_ACTION_LIKE,
post: updatedPost
};
};
This is the smReducer which accepts the action.
const initialState = {
post: {}
};
const smReducer = (state = initialState, action) => {
switch (action.type) {
case SM_ACTION_LIKE:
return {
...state,
post: action.post
};
break;
default:
return state;
}
return state;
};
export default smReducer;
Now I want to change the posts array of mainFeedReducer from here. My mainFeedReducer is this. I want to access the posts array of mainFeedReducer from smReducer.
const initialState = Immutable({
posts: [],
featuredWorkouts: []
});
const mainfeedReducer = (state = initialState, action) => {
switch (action.type) {
case FETCH_MAIN_FEED:
return {
...state,
posts: action.mainFeedData.posts,
featuredWorkouts: action.mainFeedData.featuredWorkouts
};
break;
default:
return state;
}
};
How can I achieve this?
Redux architecture revolves around a strict unidirectional data flow.
The correct way would be to design your reducers in such a way that they handle more data.
As mentioned in the docs
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.
Alternatives
You may consider using redux-thunk, since the inner
function, that recieves two parameters return (dispatch, getState),
has an access to the entire state map.
If you have an instance of your store object, then you can directly access at the states by doing store.getState()

How do I add an element to array in reducer of React native redux?

How do I add elements in my array arr[] of redux state in reducer?
I am doing this-
import {ADD_ITEM} from '../Actions/UserActions'
const initialUserState = {
arr:[]
}
export default function userState(state = initialUserState, action)
{
console.log(arr);
switch (action.type)
{
case ADD_ITEM:
return {
...state,
arr: state.arr.push([action.newItem])
}
default:
return state
}
}
Two different options to add item to an array without mutation
case ADD_ITEM :
return {
...state,
arr: [...state.arr, action.newItem]
}
OR
case ADD_ITEM :
return {
...state,
arr: state.arr.concat(action.newItem)
}
push does not return the array, but the length of it (docs), so what you are doing is replacing the array with its length, losing the only reference to it that you had. Try this:
import {ADD_ITEM} from '../Actions/UserActions'
const initialUserState = {
arr:[]
}
export default function userState(state = initialUserState, action){
console.log(arr);
switch (action.type){
case ADD_ITEM :
return {
...state,
arr:[...state.arr, action.newItem]
}
default:return state
}
}
If you need to insert into a specific position in the array, you can do this:
case ADD_ITEM :
return {
...state,
arr: [
...state.arr.slice(0, action.pos),
action.newItem,
...state.arr.slice(action.pos),
],
}
Since this question gets a lot of exposure:
If you are looking for the answer to this question, there is a good chance that you are following a very outdated Redux tutorial.
The official recommendation (since 2019) is to use the official Redux Toolkit to write modern Redux code.
Among other things, that will eliminate string action constants and generate action creators for you.
It will also employ methods that allow you to just write mutating logic in your Reducers created by createReducer or createSlice, so there is no need to write immutable code in Reducers in modern Redux in the first place.
Please follow the official Redux tutorials instead of third-party tutorials to always get the most up-to-date information on good Redux practices and will also show you how to use Redux Toolkit in different common scenarios.
For comparison, in modern Redux this would look like
const userSlice = createSlice({
name: "user",
initialState: {
arr:[]
},
reducers: {
// no ACTION_TYPES, this will internally create a type "user/addItem" that you will never use by hand. You will only see it in the devTools
addItem(state, action) {
// you can use mutable logic in createSlice reducers
state.arr.push(action.payload)
}
}
})
// autogenerated action creators
export const { addItem } = slice.actions;
// and export the final reducer
export default slice.reducer;
If you want to combine two arrays, one after another then you can use
//initial state
const initialState = {
array: [],
}
...
case ADD_ARRAY :
return {
...state,
array: [...state.array, ...action.newArr],
}
//if array = [1,2,3,4]
//and newArr = [5,6,7]
//then updated array will be -> [1,2,3,4,5,6,7]
...
This Spread operator (...) iterates array element and store inside the array [ ] or spreading element in the array, what you can simply do using "for loop" or with any other loop.
I have a sample
import * as types from '../../helpers/ActionTypes';
var initialState = {
changedValues: {}
};
const quickEdit = (state = initialState, action) => {
switch (action.type) {
case types.PRODUCT_QUICKEDIT:
{
const item = action.item;
const changedValues = {
...state.changedValues,
[item.id]: item,
};
return {
...state,
loading: true,
changedValues: changedValues,
};
}
default:
{
return state;
}
}
};
export default quickEdit;
The easiest solution to nested arrays is concat():
case ADD_ITEM:
state.array = state.array.concat(action.paylod)
return state
concat() spits out an updated array without mutating the state. Simply set the array to the output of concat() and return the state.
This worked for me
//Form side
const handleSubmit = (e) => {
e.preventDefault();
let Userdata = { ...userdata, id: uuidv4() };
dispatch(setData(Userdata));
};
//Reducer side
const initialState = {
data: [],
};
export const dataReducer = (state = initialState, action) => {
switch (action.type) {
case ActionTypes.SET_DATA:
return { ...state, data: [...state.data, action.payload] };
default:
return state;
}
};

Using dispatched actions

I have this on the bottom of my main file:
export default connect(state => ({
state: state.firebaseReducer
}),
(dispatch) => ({
actions: bindActionCreators(firebaseActions, dispatch)
})
)(Routing);
And this is my reducer that I imported
const initialState = null;
export function firebaseRef(state = initialState, action) {
switch (action.type) {
case 'FIREBASE_REF_SET':
return action.value;
default:
return state;
}
}
Action:
export function setFirebaseRef(ref) {
return {
type: 'FIREBASE_REF_SET',
value: ref
};
}
But when I try to do this in my main file:
componentDidMount() {
state.setFirebaseRef(ref);
}
It says "Can't find variable: state" . Am I calling it the wrong way? I want to be able to call the setFireBaseRef action.
connect injects parts of state and dispatch into props. You want to dispatch the action using:
this.props.dispatch(setFireBaseRef(ref))
Or, assuming firebaseActionscontains setFireBaseRef, you can just do:
this.props.actions.setFireBaseRef(ref)