HiveBrain v1.2.0
Get Started
← Back to all entries
snippetjavascriptMinor

Redux reducer with a filter

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
reducerfilterwithredux

Problem

I'm wondering if my approach to creating a filtered array (filteredLaunches) from an array of data (launches) is an anti-pattern, or not? Is there a better way to go about it?

Here is the code:



`const initialState = {
launches: [],
filteredLaunches: []
};

export default function(state = initialState, action) {

switch (action.type) {

case 'getLaunches':
return Object.assign({}, state, {
launches: action.launches
});

case 'filterLaunches':

let filteredLaunches = [];

for (let i = 0; i

Solution

Well, you could use Array.filter to filter the launches.

...
case 'filterLaunches':
  return Object.assign({}, state, {
    filteredLaunches: state.launches.filter(function(launch) {
      return action.location === launch.location.countryCode
    })
  }
default:
 return state;


The other question is, why not filtering inside the Component directly?

Because your next problem will be, that each time you add launches, you will have to filter them and copy again, and again.

So to not create duplicates, it's better to set the filter in the reducer and perform the filtering inside the render

So:

export default function(state = initialState, action) {
  case 'SET_LAUNCHES':
    return {...state, launches: action.launches };
  case 'SET_FILTER':
    return {...state, filter: action.filter };
  default:
    return state;
}

export function filterLaunches(launches, filter = {location: 'ES'} ) {
  const filterKeys = Object.keys(filter);
  return launches.filter((launch) => {
    return filterKeys.reduce((acc, key) => acc && launch[key] === filter[key], true)
  });
}

// Then in the component
import { filterLaunches } from '../launchesStore';

const LaunchesList = ({launches, filter}) => (
  
    {filterLaunches(launches, filter).map((launch) => (
      Launch: {launch.name}
    ))}
  
);

export default connect(LaunchesList);


It's good to keep the filterLaunches function next to the reducer, because this function is very related to this part of the state and if you change the format like from an array to a map-by-id you would have to change that function only in one place.

Code Snippets

...
case 'filterLaunches':
  return Object.assign({}, state, {
    filteredLaunches: state.launches.filter(function(launch) {
      return action.location === launch.location.countryCode
    })
  }
default:
 return state;
export default function(state = initialState, action) {
  case 'SET_LAUNCHES':
    return {...state, launches: action.launches };
  case 'SET_FILTER':
    return {...state, filter: action.filter };
  default:
    return state;
}

export function filterLaunches(launches, filter = {location: 'ES'} ) {
  const filterKeys = Object.keys(filter);
  return launches.filter((launch) => {
    return filterKeys.reduce((acc, key) => acc && launch[key] === filter[key], true)
  });
}

// Then in the component
import { filterLaunches } from '../launchesStore';

const LaunchesList = ({launches, filter}) => (
  <ul>
    {filterLaunches(launches, filter).map((launch) => (
      <li>Launch: {launch.name}</li>
    ))}
  </ul>
);

export default connect(LaunchesList);

Context

StackExchange Code Review Q#156970, answer score: 5

Revisions (0)

No revisions yet.