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
Related
I am implementing a shop cart using react-redux.
I got two reducers,
1.To fetch cart data from DB
2. To Carry out various cart operations.
My doubt is after achieving data from DB through the first reducer, how will I access that data through the 2nd reducer in order to carry out different cart operations ?
Reducer 1 - Fetch Data from DB
const initialState={
loading:false,
items:[],
error:false
}
const CartFetch=(state=initialState,action)=>{
switch(action.type){
case FETCHDATA : return {
...state,loading:true ,error:false
};
case FETCHSUCCESS: return {
...state,loading:false,
items:[...action.payload]
};
case FETCHERROR : return {
...state,loading:false,error:true
};
default: return state;
}
}
Fetch Actions
const fetch=()=>{
return {
type:FETCHDATA
}
}
const success=(user)=>{
return {
type:FETCHSUCCESS,
payload:user
}
}
const error=()=>{
return {
type:FETCHERROR
}
}
const fetchCartData=()=>{
const {id}=getCurrentUser();
return (dispatch)=>{
dispatch(fetch());
axios.get(`${api.userOperations}/cart/${id}`,{
headers:{'Authorization': getJwt()}
}).then(({data})=>{
dispatch(success(data));
}).catch(()=>{
dispatch(error())
})
}
}
Reducer 2 - Cart Operations
const CartHandle=(state= ..?.. ,action)=>{
switch(action.type){
case ADD_TO_CART :
return {
......
};
case INCREMENT_CART : return {
....
};
case DECREMENT_CART: return {
......
};
case REMOVE_FROM_CART : return {
.....
};
default: return state;
}
}
}
Here in Reducer 2 how will I access the pass the data which I fetched in Reducer 1 ? Or easy there any better way of implementing what I m trying to ?
Combine Reducers
const allReducer=combineReducers({
Cart:CartFetch,
CartOperations: CartHandle
});
Store
const countStore=createStore(allReducer,applyMiddleware(thunk));
<Provide store={store}>
...App.js...
</Provider>
Issue
It seems you don't quite fully understand what a reducer represents. Each reducer represents a specific "chunk" or slice of state. No two reducers function/operate on the same slice of state. In other words, two separate reducers equals two separate slices of state.
Solution
Since a reducer represents a specific slice of state it needs to handle all the actions that are associated with that slice. You just need to merge your second reducer into the first on so it fully manages the cart state.
const initialState = {
loading: false,
items: [],
error: false
};
const cartReducer = (state = initialState, action) => {
switch (action.type) {
case FETCHDATA:
return {
...state,
loading: true,
error: false
};
case FETCHSUCCESS:
return {
...state,
loading: false,
items: [...action.payload]
};
case FETCHERROR:
return {
...state,
loading: false,
error: true
};
case ADD_TO_CART:
return {
// ......
};
case INCREMENT_CART:
return {
// ....
};
case DECREMENT_CART:
return {
// ......
};
case REMOVE_FROM_CART:
return {
// .....
};
default:
return state;
}
};
Create your root reducer, each combined reducer represents a slice of state.
const allReducer = combineReducers({
// ... other state slice reducers
cart: cartReducer,
// ... other state slice reducers
});
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}
}
So this is my current reducer:
import { Reducer } from 'redux';
import {
EventState,
LOAD_EVENTS,
LOAD_EVENT_BY_ID,
FETCH_MORE_EVENTS
} from '../types/eventTypes';
export const initialState = {
eventsList: [],
event: undefined,
isLastPage: false
};
const eventReducers: Reducer<EventState, any> = (
state = initialState,
action
) => {
switch (action.type) {
case LOAD_EVENTS:
return {
...state,
eventsList: action.eventsList
};
case FETCH_MORE_EVENTS:
return {
state,
eventsList: state.eventsList.concat(action.eventsList),
isLastPage: action.eventsList.length === 0
};
default:
return state;
}
};
export default eventReducers;
As you see both cases LOAD_EVENTS and FETCH_MORE_EVENTS share the key eventsList, on fetch more events I am calling state like this state instead of ...state because it seems to re init the state of the whole reducer. But, is that the proper way? I think that if this reducer grows up, that will be a bug.
So what can I do to clean that reducer properly to make? Like all I need is that LOAD_EVENTS fires then eventsList should get clear and fill out again by what LOAD_EVENTS brings. And basically I only need to reset the state of eventsList but rest should remain the same.
when you calling state like state instead of ...state, you aren't re-init the state, but storing the previous state inside the new state, like this example below:
state = {
eventsList: [...someEvents],
event: undefined,
isLastPage: false,
state: {
eventsList: [...someEvents],
event: undefined,
isLastPage: false,
state: {
eventsList: [...someEvents],
event: undefined,
isLastPage: false
}
}
};
This is not a good pattern/practice, only if is super necessary.
So the correct, it's reset the previous state with initialState when fetch more events.
export const initialState = {
eventsList: [],
event: undefined,
isLastPage: false
};
const eventReducers: Reducer<EventState, any> = (
state = initialState,
action
) => {
switch (action.type) {
case LOAD_EVENTS:
return {
...state,
eventsList: action.eventsList
};
case FETCH_MORE_EVENTS:
return {
...initialState,
eventsList: state.eventsList.concat(action.eventsList),
isLastPage: action.eventsList.length === 0
};
default:
return state;
}
};
But how you say, it's only need to reset the state of eventsList but rest should remain the same, you can keep the same to this reducer:
case LOAD_EVENTS:
return {
...state,
eventsList: action.eventsList
};
Because when you set eventsList like the example above, you are reset the eventsList and fill out again with new data. But don't forget the problem about the first example that I say.
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'm using redux and redux-thunk long time, Im trying now this simple workflow but not working the my expected value
actioncreator =>
export const openguidelist = () => {
return dispatch => {
dispatch({ type: OPEN_GUIDE_LIST });
};
};
My reducer =>
const INITIAL_STATE = {
guideopen: true
};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case OPEN_GUIDE_LIST:
return { ...state, guideopen: true ? false : true };
default:
return state;
}
};
and triggered actioncreator the my component onPress is nothing wrong, by the way Im looking react-native-debugger, initial work is working change the guideopen true to false my expected then guideopen is never change always return false,what I'm expecting is the change in value each time the onpressing triggers but redux don't just change the state first time and than nothing change the guideopen return always the same value (false) I don't understand why please explain me
true ? false : true will always evaluate to false.
It should be guideopen: !state.guideopen.
Also, if guideopen is the only state in the reducer, you can remove the nesting and use the boolean directly as the state:
(state = false, action) {
...
case OPEN_GUIDE_LIST:
return !state;