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.
Related
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;
}
}
If I fetch this array of restos with redux:
[{
res_id: Int,
res_name: String,
res_category: String,
res_category_id: Int,
city_id: Int
}]
My action looks something like this:
export const getrestos = () => {
const resData = await response.json();
dispatch({
type: GET_RESTOS,
payload: resData
});
};
};
export const setFilters = filterSettings => {
console.log(filterSettings);
return { type: SET_FILTERS, filters: filterSettings };
};
And this is my reducer:
import { GET_RESTOS, SET_FILTERS } from '../actions/restos';
const initialState = {
restoList: [],
filteredRestos: []
};
export default (state = initialState, action) => {
switch (action.type) {
case GET_RESTOS:
return {
restoList: action.payload
}
case SET_FILTERS:
const appliedFilters = action.filters;
const updatedFilteredRestos = state.restoList.filter(resto => {
if (appliedFilters.cityID || resto.city_id) {
resto => resto.city_id.indexOf(cityID) >= 0
return { ...state, filteredRestos: updatedFilteredRestos };
}
});
return { ...state, filteredRestos: updatedFilteredRestos };
default:
return state;
}
};
I have touchable categorys in a page, and when i touch one i want to fetch the corresponding restos for that category and show them in a flatlist. Apart from that i want to have a search bar that when I type I want to show restos by res_name and/or by res_category.
Ive tried to create selectors, but I dont understand how, i dont need an specific approach, but the most clean or efficient as possible.
Thanks in advance if anyone can give me a hint or solution!
EDIT
The problem is im getting undefined in updatedFilteredRestos.
Your reducers should be clean, dumb and all they do should be returning objects. This makes your components more testable and errors easier to catch. In my opinion, this is a perfect use-case for reselect. Here's a medium article: https://medium.com/#parkerdan/react-reselect-and-redux-b34017f8194c But the true beauty of reselect is that it will memoize for you, i.e. if your states don't change, it uses a cached version of the data.
Anyway, you should clean up your restoReducer to something to this effect.
import { GET_RESTOS, SET_FILTERS } = "../actions/restos";
const initialState = {
restoList: [],
filteredRestos: []
};
const restoReducer = (state = initialState, action) => {
switch(action.type) {
case GET_RESTOS:
return { ...state, restoList: action.payload };
case SET_FILTERS:
return { ...state, filteredRestos: action.payload };
default:
return state;
}
}
Then write your filtered resto selector:
// ../selectors/restos
import { createSelector } from "reselect";
// First, get your redux states
const getRestos = (state) => state.restos.restoList;
const getFilteredRestos = (state) => state.restos.filteredRestos;
// Next, create selectors
export const getFilteredRestoList = createSelector(
[getRestos, getFilteredRestos],
(restoList, filteredRestos) => {
// need to check for non-empty filters
// if it is, simply return the unfiltered `restoList`
if(!Array.isArray(filteredRestos) || !filteredRestos.length)
return restoList || [];
// If you do have valid filters, return filtered logic
return restoList.filter(r => filteredRestos.some(f => f.cityID === r.city_id));
);
Then, use this selector in your components:
// ../components/my-app
import { getFilteredRestoList } from "../selectors/restos";
// hook it up to your `mapStateToProps` as you would a normal state
// except this time, it's a special selector
const mapStateToProps = (state, ownProps) => {
restoList: state.restos.restoList,
filteredRestos: state.restos.filteredRestos,
filteredRestoList: getFilteredRestoList(state) //<-- this is your selector
}
Then inside your component, just reference it: this.props.filteredRestoList.
I came across these reducers in the codebase I am working on at my job.
const ACTION_HANDLERS = {
[LOGIN_REQUEST]: (state, action) => ({
...state,
isAuthenticating: true
}),
[LOGIN_SUCCESS]: (state, action) => ({
...state,
isAuthenticating: false,
isAuthenticated: true,
userId: action.userId,
authToken: action.auth,
authTTL: action.ttl,
authCreatedAt: action.created,
isNewUser: action.isNewUserFlag
}),
};
export default function authReducer(state = initialAuthState, action) {
const handler = ACTION_HANDLERS[action.type];
if(handler!==undefined){
console.log('login handler',handler);
// debugger;
}
return handler ? handler(state, action) : state;
}
My concern is related to how to debug this method of pre-written reducers.
I introduced console logs before every [] in ACTION_HANDLERS but they are syntactically wrong.
I had written reducers before and they were like this.
export default function FundsReducer(state=INITIAL_STATE,action={}){
switch(action.type){
case GET_ALL_FUNDS_FAILED:{
return{
...state,
funds:{
...state.funds,
failed:true,
pending:false,
}
};
}
case GET_ALL_FUNDS_PENDING:
{
let {options}=action.payload;
return{
...state,
funds:{
...state.funds,
data:[],
failed:null,
pending:true,
}
};
}
case GET_ALL_FUNDS:
{
let data;
data=action.payload.response.data;
return{
...state,
funds:{
...state.funds,
data,
pending:false,
}
}
}
I am having difficulty in debugging these reducers and introducing console logs .
You can use redux middleware as mentioned by #remix23 or just change your action as below so you will able to log state or action.
[LOGIN_REQUEST]: (state, action) => {
console.log(action);
return {
...state,
isAuthenticating: true
}
}
Hope this will help you.
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;
}
I'm having surprisingly difficult time figuring this out, essentially I'm trying to set state to initial state, so far I tried:
// -- Initial state ------------------------------------------------------------
const INITIAL_STATE = {
search: {
listings: []
},
listings: []
}
// -- Story structure for story editor -----------------------------------------
export default function(state = INITIAL_STATE, action) {
switch(action.type) {
case ACTIONS.RESET_STATE:
return { ...state, INITIAL_STATE }
default:
return state;
}
}
this just adds initial state to existing one
case ACTIONS.RESET_STATE:
return { ...state, state = INITIAL_STATE }
this returns error
case ACTIONS.RESET_STATE:
return { ...state, state: INITIAL_STATE }
this is adding initial state to existing one gain
case ACTIONS.RESET_STATE:
return { ...state, search: { listings:[] }, listings: [] }
This works, but I start getting weird mutation errors.
The proposed solution of Anders is right, but has potential problem with immutables. This generates always new object.
case ACTIONS.RESET_STATE:
return { ...INITIAL_STATE };
Look at Jiri Fornous solution instead, as this will mutate your data.
An even easier way is to just return INITIAL_STATE.
case ACTIONS.RESET_STATE:
return INITIAL_STATE;
If you simply want to reset state completely, just return the value of INITIAL_STATE:
export default function(state = INITIAL_STATE, action) {
switch(action.type) {
case ACTIONS.RESET_STATE:
return {
search: {
listings: []
},
listings: []
};
default:
return state;
}
}
If you want to keep the INITIAL_STATE in a single place. Change the initial state creator to a function:
function get_INITIAL_STATE => {
return { search: {
listings: []
},
listings: []
}
}
export default function(state = get_INITIAL_STATE(), action) {
switch(action.type) {
case ACTIONS.RESET_STATE:
return get_INITIAL_STATE();
default:
return state;
}
}