I'm trying to delete an element from dom by clicking on it. I did it without the problem without redux thunk but now I have a problem. My reducer doesn't know about the state. How do let him know what items are?
Action:
export function deleteItem(index) {
return {
type: 'DELETE_ITEM',
index
};
}
My reducer that shows undefined.
export function deleteItem(state = [], action) {
switch (action.type) {
case 'DELETE_ITEM':
const copy = state.items.slice()
console.log(copy)
default:
return state;
}
}
Heres my actual code https://github.com/KamilStaszewski/flashcards/tree/develop/src
I saw your code and you are defining a new reducer for each of the operations you want to get done to your items (e.i itemsHaveError, deleteItem, ...) but the correct way of doing this is to store all of the relevant functions for the items to a single reducer which holds the data needed to change whenever some action to the items happens, but in the way you did it, any time any action happens because your reducers are separated the initial state gets empty as you have passed to the functions and the reducers do not know about their related data so they overwrite them with the empty initial state, the correct way would be like this to write a single reducer for items:
const initialState = {
isLoading: false,
hasError: false,
items: [],
};
export default function(state = initialState, action) {
switch (action.type) {
case ITEMS_HAVE_ERROR:
return {
...state,
hasError: action.hasError,
};
case ITEMS_ARE_LOADING:
return {
...state,
isLoading: action.isLoading,
};
case ITEMS_FETCH_DATA_SUCCESS:
return {
...state,
items: action.items,
};
case DELETE_ITEM:
const copy = state.items.slice()
return {
...state,
items: copy,
};
default:
return state;
}
}
so this would be your item.js and your item reducer and the only one that should get to combineReducer function.
Indicate the initial State of the reducer by default , the state is an empty array and you can't access the state.items , cause it is undefined. Assume this:
const x = [];
x.foo.slice();
that would return an error . Thus from :
state = []
change it to :
state = {
items:[]
}
applying it to your code:
export function deleteItem(
state = {
items:[]
},
action) {
switch (action.type) {
case 'DELETE_ITEM':
const copy = state.items.slice()
console.log(copy)
default:
return state;
}
}
Related
I am having a two step form in react. The first step of the form we ask some information to the user and then I add it to the state. The second step of the form I ask some more information to the user and add it to the state, so instead of appending the information that was asked on step 2 of the form, it overrides the state, so the state now only the info that was asked in the step 2 of the form. How can I add the have both the information together. When i try to ...state it gives me error as state is not iterable.
const infoReducer = (state = {}, action) => {
switch(action.type) {
case 'STEP_1':
return action.payload
case 'STEP_2':
return action.payload
default:
return state
}
}
check this out
import { ActionTypes } from "../../Constant/ActionType";
const initState = {
Auth: {},
};
const AuthReducer = (state = initState, action) => {
switch (action.type) {
case ActionTypes.AuthUser:
return {
...state,
Auth: action.payload,
};
default:
return state;
}
};
export default AuthReducer;
first, copy the current state data and add a new action payload as second parameter
case 'ADD_Data':
return {...state,action.payload}
I've been working with redux for the last couple weeks and was incorporating it into my projects when I ran into this wall. Pretty common reducer for modals being rendered so i can animate them before unmounting them.
const initialState = {
isModalOpen: false,
test: false
}
export default function(state = initialState, action) {
switch (action.type) {
case "modalInteraction":
return {
isModalOpen: action.payload
};
case "testModalInteraction":
return {
test: action.payload
};
default:
return state;
};
}
Sadly, the test property is still returning as undefined despite the fact that the other initial state in the same reducer can be called without a problem. I even removed all the testModalInteraction dispatches in the case that that somehow upset the datatype. I just can't spot the difference that keeps returning undefined.
When you return the new state, make sure to spread the initial state (...state) and then change whatever values you need to change.
const initialState = {
isModalOpen: false,
test: false
}
export default function(state = initialState, action) {
switch (action.type) {
case "modalInteraction":
return {
...state,
isModalOpen: action.payload
};
case "testModalInteraction":
return {
...state,
test: action.payload
};
default:
return state;
};
}
If it is still undefined, make sure the payloads are defined for both actions.
For example, your modalInteraction action could look like
export const modalInteraction = (bool) => ({
type: "modalInteraction",
payload: bool
})
P.S., you can destructure the action object. This allows you to use "type" instead of "action.type" and "payload" instead of "action.payload".
const initialState = {
isModalOpen: false,
test: false
}
export default function(state = initialState, action) {
const {type, payload} = action;
switch (type) {
case "modalInteraction":
return {
...state,
isModalOpen: payload
};
case "testModalInteraction":
return {
...state,
test: payload
};
default:
return state;
};
}
I tried lot of things but I can't achieved what want. I have an initialState in redux. It has 4 coin name as you see below. I'm trying to fill this keys with value from API. But at the end I have only 1 key with data.
import { SET_LIST, GET_LIST } from '../actions'
const initialState = {
coins: {
'BTC': {},
'ETH': {},
'LTC': {},
'DOGE': {},
},
loading: false,
error: null
}
const coinsReducers = (state = initialState, action) => {
switch (action.type) {
case SET_LIST: {
const key = action.payload.key;
const list = action.payload.list;
let obj = Object.assign({}, state);
obj.coins[key] = list;
obj.loading = true;
return obj;
}
default: return state;
}
}
export default coinsReducers
I iterate this initial state in app.js componentDidMount hook and make api call with key. When I make api call with BTC key, I want to push the response into BTC key.
I hope someone help me.
EDIT: Working Example
case SET_LIST: {
const {key, list} = action.payload;
return { ...state, loading: true, coins: {...state.coins, [key]: list }};
}
case GET_LIST:
// don't sure what you try to achieve here, anyhow what you are saying is that
// from now on all the state of the app it just state.coins
return state.coins
default: return state;
The reducer is used to update the state, not to get data from it.
The GET_LIST action returns state.coins which corrupts the state object, thus returning wrong values
Use store.getState() to get data from store
I have been playing with react-native and redux and I have encountered an error. The state is always initialised as the starting one although the payload is present when I debug it inside the reducer.
This is my reducer file
let initialState = {
filterList: [],
isFetching: false,
activeFilters: [],
}
export function fetchFilterList(state = initialState, action) {
return { ...state, isFetching: true };
}
export function fetchFilterListSuccess(state, action) {
return {
...state,
filterList: action.payload,
isFetching: true,
dsad: "dada",
}
}
export function fetchFilterListError(state, action) {
return { ...state, isFetching: false };
}
This is where I combine them into one function(in the same file as above):
export function combinedFiltersReducers(state = initialState, action) {
switch (action.type) {
case ACTION_TYPES.FETCH_FILTER_LIST:
return fetchFilterList(state, action);
case ACTION_TYPES.FETCH_FILTER_LIST_SUCCESS:
return fetchFilterListSuccess(state, action);
case ACTION_TYPES.TOGGLE_FILTER_ITEM:
return toggleFilterItemStart(state, action);
case ACTION_TYPES.TOGGLE_FILTER_ITEM_SUCCESS:
return toggleFilterItemSuccess(state, action);
default:
return state;
}
}
This is my combine reducers function in a separate file called main reducers.
export default combineReducers({
adList: fetchAdListSuccess,
filterList: combinedFiltersReducers,
});
Here is the where I receive the state in the component, and it always falls to the initial state.
const mapStateToProps = state => ({
filterList: state.filterList,
});
const mapPropsToDispatch = dispatch => ({
fetchFilterList:() => dispatch(fetchFilterList()),
toggleFilterItem: (data) => dispatch(toggleFilterItem(data)),
});
export default connect(
mapStateToProps,
mapPropsToDispatch
)(FilterComponent);
I can not find the error, so I need a bit of help. Thanks in advance.
Okay, I have found an issue. Taking an example:
return fetchFilterList(state, action);
You're calling fetchFilterList method and passing initial state. So every time you call it actually passes the initial state. And that method is just copying the initial state. Rather do like this:
return fetchFilterList(...state, action);
I am going to assume that your ACTION_TYPES do not have the appropriate type(s) for the function you are calling so it is reverting to the default case.
Is it okay to add more info to an action so component specific reducers (and sagas/whatever side effect lib you're using) can filter them?
Example:
function reducerComponentA(state, action) {
switch (action.type) {
case START_FETCH:
return {
...state,
isLoading: true,
};
break;
case START_FETCH_SUCCESS:
return {
...state,
isLoading: false,
};
break;
}
return state;
}
and
function reducerComponentB(state, action) {
switch (action.type) {
case START_FETCH:
return {
...state,
isLoading: true,
};
break;
case START_FETCH_SUCCESS:
return {
...state,
isLoading: false,
};
break;
}
return state;
}
Notice how both reducers observes the same action and act on them (show a loading animation). Now if the screen/component that these reducers are related to are both in memory, the START_FETCH will cause to both of them to show the loading animation, maybe even overlapping (because it's global). Is filtering actions by screen/component a good solution?
Like this:
function reducerComponentA(state, action) {
if (action.currentScreen === 'ScreenA') {
switch (action.type) {
...
}
}
return state;
}
This seems to more of a problem on React Native, because if you're using a Navigator, there's a chance multiple screens will be loaded at the same time.
You can 'mount' reducer to the different slices of the state. To achieve this, you can add path to the action, and in the reducer, update corresponding slice of the state.
It can be similar to:
function reducer(state, action) {
if (action.type === '...') {
return _.set(_.deepClone(state), `${action.path}.isLoading`, false)
} else return state;
}
In other words, action determines which part of the state reducer will be operating with.
Note that this example above is extremely inefficient and only for demo purpose. Instead of cloning the state, some immutability helpers should be used: kolodny/immutability-helper, mweststrate/immer, other.
UPD
Imagine you have action and reducer for an input state:
const UPDATE_VALUE = 'UPDATE_VALUE';
const updateValue = (value) => ({ type: UPDATE_VALUE, value })
function reducer(state, action) {
if (action.type === UPDATE_VALUE) {
return { ...state, input: action.value }
} else return state;
}
And you want to use this action/reducer for many different inputs. The action can be supplied with
a property path that indicates which part or the state should be updated, and eventually which input
will receive new props:
const UPDATE_VALUE = 'UPDATE_VALUE';
const updateValue = (value, path) => ({ type: UPDATE_VALUE, value, path })
function reducer(state, action) {
if (action.type === UPDATE_VALUE) {
return { ...state, [action.path]: action.value }
} else return state;
}
This can be used then:
dispatch(updateValue(event.target.value, 'firstNameInput'))
dispatch(updateValue('Doe', 'lastNameInput'))
The code at the beginning of the answer is a generic version of the latter.