can we pass the value directly to the state in redux reducer like
export default (state = [], action) => {
switch (action.type) {
case 'FETCH_USER':
return [...state, action.payload];
default:
return state;
}
}
here i added action payload value to state , i was wondering is it safe to do that ?
if i only return acion.payload , i would get a error that says
state.users.find is not a function
here is mapStateToProps function
const mapStateToProps = (state, ownProps) => {
return { user: state.users.find(user => user.id === ownProps.userId) };
}
what is the best practice?
if you need more information please let me know
You should define an initial state in your reducer like:
const initialState = {
users: []
}
Then initialize the state with the initial state, and update it with your payload:
export default (state = initialState, action) => {
switch (action.type) {
case 'FETCH_USER':
return {...state, users: action.payload};
default:
return state;
}
}
Related
I want process a plain boolean value with my Redux store. But this value results into undefined on every dispatch event. Does any of you see why?
I have the following setup:
reducer.js
const initialState = {
canSwipe: true
};
export default function rootReducer(state = initialState, action) {
switch (action.type) {
case CAN_SWIPE: {
console.log(action.payload.canSwipe) // This logs true or false - works!
return action.payload.canSwipe
}
default:
return state;
}
}
actions.js
export const setSwipeState = canSwipe => ({ type: CAN_SWIPE, payload: { canSwipe } });
component for dispatching
function MapOverlay({setSwipeState}) {
const zoom = 5;
return (
<Map
onMovestart={() => setSwipeState(false)}
onMoveend={() => setSwipeState(true)}
>
{...}
</Map>
)
}
export default connect(null, {setSwipeState})(MapOverlay);
The reducer should return data which must be compliant with your initial state:
const initialState = {
canSwipe: true
};
So you need to change your reducer to respect this structure:
switch (action.type) {
case CAN_SWIPE: {
console.log(action.payload.canSwipe) // This logs true or false - works!
return {canSwipe: action.payload.canSwipe}
}
In my code, action.payload points to a todo.index, so my REMOVE_TODO case should filter out a todo if it has the same index as the one the event was fired on. However, I get syntax errors all over. I have messed with my wrappings and even tried rewriting it from scratch but cannot get these syntax errors away. Does someone see something I'm missing here??
Just let me know if any more information is needed please. Thank you all!
import { ADD_TODO } from '../constants/action-types';
import { REMOVE_TODO } from '../constants/action-types';
const initialState = {
todos: []
};
const rootReducer = (state = initialState, action) => {
switch (action.type) {
case ADD_TODO:
return {
...state,
todos: [...state.todos, action.payload]
}
case REMOVE_TODO:
return {
...state,
todos: [...state.todos.filter((todo, i) => {i !== action.payload})]
}
default:
return state;
}
}
export default rootReducer;
Well you missed returning a value inside filter function.
Have a look at the demo.
const initialState = {
todos: [1,2,3,7,8,5]
};
const rootReducer = (state = initialState, action) => {
switch (action.type) {
case 'ADD_TODO':
return {
...state,
todos: [...state.todos, action.payload]
}
case 'REMOVE_TODO':
return {
...state,
todos: [...state.todos.filter((todo, i) => i !== action.payload)]
}
default:
return state;
}
}
let op = rootReducer({todos:[1,2,3,4,5,6]},{type:'REMOVE_TODO',payload:1});
console.log(op)
Turns out I was simply returning an object from my filter method. todos: [...state.todos.filter((todo, i) => {i !== action.payload})]
Just took away the curly brackets from the return on the arrow function and it's all good now.
I am experiencing an issue with React Native whilst using Redux.
I am using a Redux state to show/hide a modal from one component to the other. As this seems to be the best solution considering that it is cross component.
I have the modal opening and closing perfectly fine, and that works exactly how it show. However, when I click on this, it seems as though the props for the parent component are getting updated to the initial state again and I'm unsure as to why.
Parent Component:
const mapStateToProps = state => {
return {
modalVisible: state.modals.addRoomModalVisible
}
};
const mapDispatchToProps = dispatch => {
return {
onMakeAddRoomModalActive: () => dispatch(makeAddRoomModalVisible())
}
};
export default connect(mapStateToProps, mapDispatchToProps)(RoomsScreen);
Child Component
const mapStateToProps = state => {
return {
rooms: state.rooms.rooms
}
};
const mapDispatchToProps = dispatch => {
return {
onGetRooms: () => dispatch(getRooms())
}
};
export default connect(mapStateToProps, mapDispatchToProps)(RoomList);
Modals Reducer
import { HIDE_ADD_ROOM_MODAL, SHOW_ADD_ROOM_MODAL } from "../actions/actionTypes";
const initialState = {
addRoomModalVisible: false
};
const modalsReducer = (state = initialState, action) => {
switch (action.type) {
case SHOW_ADD_ROOM_MODAL:
return {
...state,
addRoomModalVisible: true
};
case HIDE_ADD_ROOM_MODAL:
return {
...state,
addRoomModalVisible: false
};
default:
return initialState;
}
};
export default modalsReducer;
It seems the issue lies when I call the onMakeAddRoomModalActive prop. I have console logged out and the state is getting reset and the this.props.rooms is getting set to and empty array which is the initialState object which I have defined.
The issue lay within all of my reducers.
At the end of each reducer case statement I did a default which set the state to be the initialState which was defined at the top of the reducer.
I needed to change this to return state instead.
const modalsReducer = (state = initialState, action) => {
switch (action.type) {
case SHOW_ADD_ROOM_MODAL:
return {
...state,
addRoomModalVisible: true
};
case HIDE_ADD_ROOM_MODAL:
return {
...state,
addRoomModalVisible: false
};
default:
return state;
}
};
I need to concat an array from my reducer after add to cart button is pressed.
I tried pushed, but it doesn't seem to work.
import { combineReducers } from 'redux';
import { DATA_AVAILABLE,
ADD_TO_CART,
GET_CART_DATA
} from "../actions/" //Import the actions types constant we defined in our actions
let dataState = { data: [], loading:true };
let cartState = { data: [] };
const dataReducer = (state = dataState, action) => {
switch (action.type) {
case DATA_AVAILABLE:
state = Object.assign({}, state, { data: action.data, loading:false });
return state;
default:
return state;
}
};
const cartReducer = (state = cartState, action) => {
switch (action.type) {
case ADD_TO_CART:
state = Object.assign({}, state, { data: [action.data]});
//console.log("state data => "+state.data);
return state;
default:
return state;
}
};
// Combine all the reducers
const rootReducer = combineReducers({
dataReducer,
cartReducer,
// ,[ANOTHER REDUCER], [ANOTHER REDUCER] ....
})
export default rootReducer;
During ADD_TO_CART event, the reducer is replacing all the data each time my add to cart button is clicked. Instead, I need to concat those items so I can show them into my cart list.
Seems like you probably want:
case ADD_TO_CART:
return Object.assign({}, state, {
data : state.data.concat(action.data)
});
If you have the Object Spread syntax available in your app setup (which is turned on by default if you're using Create-React-App), you can simplify that a bit to:
case ADD_TO_CART:
return {...state, data : state.data.concat(action.data) }
I seem to have hit a snag when updating state using redux and react-redux. When I update an individual slice of state, all of the others get removed. I know the answer to this will be simple but I can't figure it out and haven't found anything else online.
So to clarify, here's my reducer:
const initialState = {
selectedLevel: null,
selectedVenue: null,
selectedUnitNumber: null,
selectedUnitName: null,
selectedYear: null
}
export default (state = initialState, action) => {
console.log('reducer: ', action);
switch (action.type){
case 'CHOOSE_LEVEL':
return action.payload;
case 'CHOOSE_VENUE':
return action.payload;
case 'CHOOSE_UNIT':
return action.payload;
case 'SHOW_COURSES':
return action.payload;
}
return state;
}
And my combine reducer:
export default combineReducers({
workshopSelection: WorkshopSelectReducer
});
So my initial state looks like this:
workshopSelection: {
selectedLevel: null,
selectedVenue: null,
selectedUnitNumber: null,
selectedUnitName: null,
selectedYear: null
}
But when I use one of my action creators, for example:
export function chooseVenue(venue){
return {
type: 'CHOOSE_VENUE',
payload: {
selectedVenue: venue
}
}
}
I end up with state looking like this:
workshopSelection: {
selectedVenue: 'London',
}
All of the rest of the state within this object that wasn't affected by this action creator has been completely wiped out. Instead, I just want all other entries to stay as they are with their original values - null in this example, or whatever other value has been assigned to them.
Hope that all makes sense.
Cheers!
You are basically replacing one object (previous state) with another one (your payload, which is also an object).
In terms of standard JS, this would be the equlivalent of what your reducer does:
var action = {
type: 'CHOOSE_VENUE',
payload: {
selectedVenue: venue
}
};
var state = action.payload;
The simplest way to fix this would be using Object spread properties:
export default (state = initialState, action) => {
switch (action.type){
case 'CHOOSE_LEVEL':
case 'CHOOSE_VENUE':
case 'CHOOSE_UNIT':
case 'SHOW_COURSES':
// Watch out, fall-through used here
return {
...state,
...action.payload
};
}
return state;
}
... but since this is still in experimental phase, you have to use some other way to clone previous properties and then override the new ones. A double for ... in loop could be a simple one:
export default (state = initialState, action) => {
switch (action.type){
case 'CHOOSE_LEVEL':
case 'CHOOSE_VENUE':
case 'CHOOSE_UNIT':
case 'SHOW_COURSES':
// Watch out, fall-through used here
const newState = {};
// Note: No key-checks in this example
for (let key in state) {
newState[key] = state[key];
}
for (let key in action.payload) {
newState[key] = action.payload[key];
}
return newState;
}
return state;
}
Keep your payload object as flat on actions creators as shown below...
export function chooseVenue(venue){
return {
type: 'CHOOSE_VENUE',
selectedVenue: venue
}
}
and modify your reducer as below (given example is for updating the venue, do the same for other cases too...)
export default (state = initialState, action) => {
let newState = Object.assign({}, state); // Take copy of the old state
switch (action.type){
case 'CHOOSE_LEVEL':
case 'CHOOSE_VENUE':
newState.selectedVenue = action.selectedVenue; // mutate the newState with payload
break;
case 'CHOOSE_UNIT':
case 'SHOW_COURSES':
default :
return newState;
}
return newState; // Returns the newState;
}