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}
}
Related
Given the following 2 states as an example:
state_a: 'mode_1' | 'mode_2' | 'mode_3'
state_b: boolean
state_b can only be false whenever state_a === 'mode_2', and when state_a !== 'mode_2', state_b should return to the previous value (i.e. it's limits opened up. For example, if it was true -> false (limited) -> true or false -> false (limited) -> false).
What's the usual practice/style to define such a behavior in Redux?
If I'm understanding your question correctly you want, from the app's perspective, state_b to always be false when state_a === 'mode_2', and when state_a !== 'mode_2' state_b is whatever is stored in state.
On the surface your questions is posed in such a way that it sounds like you want to implement some logic in the reducer functions that coordinates the values between these two states when either of them update. While you could do this I suspect a simpler solution is to derive the provided state when consuming it. In other words, use a selector function to compute derived state, when the state_a value is "mode_2" then the selector function selecting state_b returns false, otherwise it returns the actual state_b state value.
Example:
import {
combineReducers,
configureStore,
createSlice,
createSelector
} from "#reduxjs/toolkit";
import { Provider, useDispatch, useSelector } from "react-redux";
const MODES = {
mode_1: "mode_1",
mode_2: "mode_2",
mode_3: "mode_3"
};
const state = createSlice({
initialState: {
state_a: MODES.mode_1,
state_b: true
},
name: "state",
reducers: {
setMode: (state, action) => {
state.state_a = action.payload;
},
toggleB: (state, action) => {
state.state_b = !state.state_b;
}
}
});
const { setMode, toggleB } = state.actions;
const selectState = (state) => state.state;
const select_a = createSelector([selectState], (state) => state.state_a);
const select_b = createSelector([select_a, selectState], (state_a, state) =>
state_a !== MODES.mode_2 ? state.state_b : false
);
Selecting the state in a component:
const state_a = useSelector(select_a);
const state_b = useSelector(select_b);
Demo:
You are looking for Sharing data between slice reducers,
import { combineReducers, createStore } from "redux";
type StateA = 'mode_1' | 'mode_2' | 'mode_3';
const stateAReducer = (state: StateA = 'mode_1', action) => {
switch (action.type) {
case 'SWITCH_MODE':
return action.payload;
default:
return state;
}
}
type StateB = boolean;
const stateBReducer = (state: StateB = true, action) => {
return state;
}
const combinedReducer = combineReducers({
stateA: stateAReducer,
stateB: stateBReducer
});
type CrossSliceState = ReturnType<typeof combinedReducer>;
const crossSliceReducer = (state: CrossSliceState, action) => {
switch (action.type) {
case 'SWITCH_MODE':
const nextStateA = stateAReducer(state.stateA, action);
return {
stateA: nextStateA,
stateB: nextStateA === 'mode_2' ? false : state.stateB
}
default:
return state;
}
}
const rootReducer = (state, action) => {
const intermediateState = combinedReducer(state, action);
const finalState = crossSliceReducer(intermediateState, action);
return finalState
}
const store = createStore(rootReducer);
store.subscribe(() => {
console.log(store.getState());
})
store.dispatch({ type: 'SWITCH_MODE', payload: 'mode_1' });
store.dispatch({ type: 'SWITCH_MODE', payload: 'mode_2' });
store.dispatch({ type: 'SWITCH_MODE', payload: 'mode_1' });
Output:
{ stateA: 'mode_1', stateB: true }
{ stateA: 'mode_2', stateB: false }
{ stateA: 'mode_1', stateB: false }
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;
}
}
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;
}
};
Total React/Redux noob here; In my app I have a form checkbox that should set an option to true or false in my state.
Here's my checkbox - I'm not sure how to set this true/false flag correctly:
<input
type="checkbox"
onChange={ (e) => this.props.dispatch(setOption({'currentAddress': [true/false flag]})) }
defaultChecked={ true }
/>
The Action - this should be reusable by other checkboxes on the form:
const SET_OPTION = 'SET_OPTION';
export const setOption = (option) => ({
type: SET_OPTION,
payload: option
})
And the reducer:
const initialState = {
formOptions {
currentAddress: true,
isEmployed: true,
// ...
}
}
const Reducer = (state = initialState, action) => {
switch (action.type) {
case SET_OPTION:
let option = action.payload
return { ...state.formOptions, option};
default:
return state;
}
}
My questions are:
How do I toggle the option between true and false in my state?
How can I reference this option later in my code? Is getState() the standard way?
Any input is appreciated!
1)
If the initial state of your store is
{
formOptions: {
currentAddress: true,
isEmployed: true
// ...
}
}
Then in the reducer don't return
{
...state.formOptions
}
Because this will return a state which looks different to the initial structure
{
currentAddress: true,
isEmployed: true
// ...
}
Read here about the spread operator behaviour: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax
Instead your reducer should look like
const reducer = (state = initialState, action) => {
switch (action.type) {
case SET_OPTION:
return {
...state, // copy data in state other than formOptions
formOptions: {
...state.formOptions, // copy other formOptions
...action.payload // here you are overwriting the 'currentAddress' property since action.payload = { 'currentAddress': true/false }
}
};
default:
return state;
}
};
Reducer is just a function which takes state and return new state :)
2)
You probably would like to bind the Redux store with a React component to be able to pass the Redux data in React component props. Full instructions are available here: https://redux.js.org/basics/usage-with-react
Try create reducers ang get data from action
But get error in console: reducer is not a function.....
My reducer:
import { INCOME_LIST } from '../actionTypes'
import Immutable from 'immutable'
const initialUserState = {
list: []
}
const listReducer = function(state = initialUserState, action) {
switch(action.type) {
case 'INCOME_LIST':
return Object.assign({}, state, { list: action.data });
}
return state;
}
Where I have mistake?
My Action :
import axios from 'axios'
import { INCOME_LIST } from '../actionTypes'
function receiveData(json) {
return{
type: INCOME_LIST,
data: json
}
};
export function IncomeList () {
return dispatch => {
return (
axios.post('http://139.196.141.166:8084/course/income/outline',{}, {
headers: { 'X-Authenticated-Userid': '15000500000#1' }
}).then(function (response) {
dispatch(receiveData(response.data));
})
)
}
}
How it right way create reducer for that?
Looks like you never exported your reducer. An export default listReducer in your listReducer.js file should do the trick.
Store - holds our state - THERE IS ONLY ONE STATE
Action - State can be modified using actions - SIMPLE OBJECTS
Dispatcher - Action needs to be sent by someone - known as dispatching an action
Reducer - receives the action and modifies the state to give us a new state
pure functions
only mandatory argument is the 'type'
Subscriber - listens for state change to update the ui
const initialState = {
counter: 0
}
const reducer = (state = initialState, action) => {
switch (action.type) {
case 'INCREASE_COUNTER':
return { counter: state.counter + 1 }
case 'DECREASE_COUNTER':
return { counter: state.counter - 1 }
}
return state
}
const store = createStore(reducer)
class App extends Component {
render() {
return (
<Provider store={store}>
<CounterApp />
</Provider>
);
}
}
In my case I called the function immediately
const store = createStore(rootReducer())
instead of
const store = createStore(rootReducer)