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;
Related
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 starting to using react hooks and i decide to put an object instead of a variable in the useState function :
const [foo, setFoo] = useState({
term: '',
loading: false,
...
});
but later when I want to update it
const onChange = (e) => {
const { value } = e.target;
setFoo({ ...foo, term: value });
if(value) setFoo({ ...foo, loading: true });
...
}
...
return (
...
<input onChange={onChange} value={term} />
...
)
1. Why in the second setFoo when I check the foo object I get alway term property equal to '' exactly like the initial value and the input don't get updated with the typed characters ?
- When I delete the second setFoo it works so I guess because setFoo is asynchronous but how to solve this issue ?.
- I know that we can workaround this issue by managing to call setFoo just once but i want to know other solutions ?
2. Why this kinda of issues never happened in redux?
The solution: use one setFoo like this:
const onChange = (e) => {
const { value } = e.target;
setFoo({ term: value, loading: !!value });
...
}
!! means "convert to boolean value".
1. Probably because you have to destruct foo instead of state
setFoo({ ...foo, term: value });
working useState example
2. Take a look at the additional hooks, especially useReducer
useReducer is usually preferable to useState when you have complex state logic that involves multiple sub-values or when the next state depends on the previous one.
And your code can become:
const reducer = (state, action) {
switch(action.type) {
case 'loading':
return { ...state, loading: true };
case 'loaded':
default:
return { ...state, loading: false };
}
}
const initialState = {
term: '',
loading: false,
}
const [state, dispatch] = useReducer(reducer, initialState);
dispatch({
type: e.target.value ? 'loaded' : 'loading'
});
working useReducer example on CodeSandbox
You can solve the issue by calling setFoo only once with all the new key-value pairs, like this:
const onChange = (e) => {
const { value } = e.target;
const loading = value ? true : false;
setFoo({ ...state, term: value, loading });
....
}
Update:
For complex state update and more control on that part, you can use useReducer, and write a separate reducer to update the 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;
}
};
I'm trying to implement a method to store in Redux session the counting of results from the base everytime the application also fetches it. The componentWillReceiveProps method is the following:
if (!countingTestAnnouncements && countingTestAnnouncementsSuccess) {
let value = parseInt(totalTests.total);
setCurrentValue(value);
}
It is clear. The method to store in the session will be executed when the counting is successful. This is the action file:
export const SET_CURRENT_VALUE = "hp:alert:set_current_value";
export function setCurrentValue(currentValue) {
return (dispatch) => {
dispatch({
type: SET_CURRENT_VALUE,
payload: currentValue
})
};
}
const ACTION_HANDLERS = {
[SET_CURRENT_VALUE]: (state, action) => {
return {
...state,
currentValue: Value, action.payload
}
}
};
const initialState = {
currentValue: null
};
export default function alertReducer (state = initialState, action) {
const handler = ACTION_HANDLERS[action.type];
return handler ? handler(state, action) : state
};
What is causing me a headache is the fact that when the return is reached within ACTION_HANDLERS, the looping will occur, and I do not know why it is happening.
const ACTION_HANDLERS = {
[SET_CURRENT_VALUE]: (state, action) => {
// By commenting out the return block and putting
// a console.log, the result will be seen only once,
// as expected. But as it is, the loop will happen.
return {
...state,
currentValue: action.payload
}
// console.log(action.payload) will display once the counting.
}
};
Why don't know why componentWillReceiveProps keeps repeating like this. What is really pissing me off is that the whole block is executed, the conditions of the if should be false and do not enter the block.
Whenever you are calling "setCurrentValue(value)" you are triggering and "componentWillReceiveProps" after the dispatch and this cause the loop.
It might be better to get this total in reducer and pass it to store or to reset countingTestAnnouncementsSuccess in order to skip it on next update like given below:
if (!countingTestAnnouncements && countingTestAnnouncementsSuccess) {
let value = parseInt(totalTests.total);
countingTestAnnouncementsSuccess = false;
setCurrentValue(value);
}
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