From 7773e560dd19f164709df73eac88b49c68e52563 Mon Sep 17 00:00:00 2001 From: jgautsch Date: Fri, 6 Nov 2015 12:01:55 -0600 Subject: [PATCH 1/2] Add very basic reselect usage --- containers/App.js | 19 ++++++++++--------- package.json | 3 ++- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/containers/App.js b/containers/App.js index 599defd..4080810 100644 --- a/containers/App.js +++ b/containers/App.js @@ -5,6 +5,7 @@ import AddTodo from '../components/AddTodo' import TodoList from '../components/TodoList' import Footer from '../components/Footer' import { memoize, createMemoizedFunction } from '../memoize' +import { createStructuredSelector } from 'reselect'; function selectTodos(todos, filter) { console.log("Recalculating selectTodos"); @@ -75,15 +76,15 @@ App.propTypes = { ]).isRequired } -// Which props do we want to inject, given the global state? -// Note: use https://github.com/faassen/reselect for better performance. -function select(state) { - return { - todos: state.todos, - visibilityFilter: state.visibilityFilter, - currentTheme: state.currentTheme - } -} +const todosSelector = state => state.todos; +const visibilityFilterSelector = state => state.visibilityFilter; +const currentThemeSelector = state => state.currentTheme; + +const select = createStructuredSelector({ + todos: todosSelector, + visibilityFilter: visibilityFilterSelector, + currentTheme: currentThemeSelector +}); // Wrap the component to inject dispatch and state into it export default connect(select)(App) diff --git a/package.json b/package.json index 5365197..64d9ed9 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,8 @@ "react": "^0.14.0", "react-dom": "^0.14.0", "react-redux": "^4.0.0", - "redux": "^3.0.0" + "redux": "^3.0.0", + "reselect": "^2.0.0" }, "devDependencies": { "babel-core": "^5.6.18", From c94131e634cd548f4662db7a7152f47f386dfc64 Mon Sep 17 00:00:00 2001 From: jgautsch Date: Fri, 6 Nov 2015 12:18:11 -0600 Subject: [PATCH 2/2] Add more complete reselect example --- actions.js | 8 ++++++ containers/App.js | 70 +++++++++++++++++++++++++---------------------- reducers.js | 14 ++++++++-- 3 files changed, 58 insertions(+), 34 deletions(-) diff --git a/actions.js b/actions.js index fab46c2..7ee5d03 100644 --- a/actions.js +++ b/actions.js @@ -6,6 +6,7 @@ export const ADD_TODO = 'ADD_TODO' export const COMPLETE_TODO = 'COMPLETE_TODO' export const SET_VISIBILITY_FILTER = 'SET_VISIBILITY_FILTER' export const CHANGE_THEME = 'CHANGE_THEME' +export const UPDATE_SEARCH = 'UPDATE_SEARCH'; /* * other constants @@ -36,3 +37,10 @@ export function setVisibilityFilter(filter) { export function changeTheme() { return { type: CHANGE_THEME }; } + +export function updateSearch(searchTerm) { + return { + type: UPDATE_SEARCH, + searchTerm + }; +} diff --git a/containers/App.js b/containers/App.js index 4080810..ce71cd8 100644 --- a/containers/App.js +++ b/containers/App.js @@ -1,55 +1,32 @@ import React, { Component, PropTypes } from 'react' import { connect } from 'react-redux' -import { addTodo, completeTodo, setVisibilityFilter, changeTheme, VisibilityFilters } from '../actions' +import { addTodo, completeTodo, setVisibilityFilter, changeTheme, VisibilityFilters, updateSearch } from '../actions' import AddTodo from '../components/AddTodo' import TodoList from '../components/TodoList' import Footer from '../components/Footer' import { memoize, createMemoizedFunction } from '../memoize' -import { createStructuredSelector } from 'reselect'; - -function selectTodos(todos, filter) { - console.log("Recalculating selectTodos"); - switch (filter) { - case VisibilityFilters.SHOW_ALL: - return todos - case VisibilityFilters.SHOW_COMPLETED: - return todos.filter(todo => todo.completed) - case VisibilityFilters.SHOW_ACTIVE: - return todos.filter(todo => !todo.completed) - } -} - -function selectMatchingTodos(todos, search) { - console.log("Recalculating matchingTodos"); - return todos.filter((todo) => { return todo.text.search(search) >= 0; }); -} +import { createSelector, createStructuredSelector } from 'reselect'; class App extends Component { - constructor(props, context) { - super(props, context); - this.state = { search: '' }; - } - - visibleTodos = createMemoizedFunction(() => [this.props.todos, this.props.visibilityFilter], selectTodos); - matchingTodos = createMemoizedFunction(() => [this.visibleTodos(), this.state.search], selectMatchingTodos); updateSearch = function(e) { - this.setState({ search: e.target.value }); + const { dispatch } = this.props; + dispatch(updateSearch(e.target.value)); } render() { console.log(this.props); // Injected by connect() call: - const { dispatch, visibilityFilter, currentTheme } = this.props + const { dispatch, matchingVisibleTodos, currentTheme, searchTerm, visibilityFilter } = this.props return (
- Search:
+ Search:
dispatch(addTodo(text)) } /> dispatch(completeTodo(index)) } /> @@ -76,14 +53,43 @@ App.propTypes = { ]).isRequired } +function selectVisibleTodos(todos, filter) { + console.log("Recalculating selectTodos"); + switch (filter) { + case VisibilityFilters.SHOW_ALL: + return todos + case VisibilityFilters.SHOW_COMPLETED: + return todos.filter(todo => todo.completed) + case VisibilityFilters.SHOW_ACTIVE: + return todos.filter(todo => !todo.completed) + } +} + +function selectMatchingTodos(todos, search) { + console.log("Recalculating matchingTodos"); + return todos.filter((todo) => { return todo.text.search(search) >= 0; }); +} + const todosSelector = state => state.todos; const visibilityFilterSelector = state => state.visibilityFilter; const currentThemeSelector = state => state.currentTheme; +const searchTermSelector = state => state.searchTerm; + +const visibleTodosSelector = createSelector( + [todosSelector, visibilityFilterSelector], + selectVisibleTodos +); + +const matchingVisibleTodosSelector = createSelector( + [visibleTodosSelector, searchTermSelector], + selectMatchingTodos +); const select = createStructuredSelector({ - todos: todosSelector, + matchingVisibleTodos: matchingVisibleTodosSelector, visibilityFilter: visibilityFilterSelector, - currentTheme: currentThemeSelector + currentTheme: currentThemeSelector, + searchTerm: searchTermSelector }); // Wrap the component to inject dispatch and state into it diff --git a/reducers.js b/reducers.js index 7c6a95b..bc73343 100644 --- a/reducers.js +++ b/reducers.js @@ -1,5 +1,5 @@ import { combineReducers } from 'redux' -import { ADD_TODO, COMPLETE_TODO, SET_VISIBILITY_FILTER, CHANGE_THEME, VisibilityFilters } from './actions' +import { ADD_TODO, COMPLETE_TODO, SET_VISIBILITY_FILTER, CHANGE_THEME, VisibilityFilters, UPDATE_SEARCH } from './actions' const { SHOW_ALL } = VisibilityFilters function visibilityFilter(state = SHOW_ALL, action) { @@ -43,11 +43,21 @@ function currentTheme(state = 'theme-green', action) { } } +function searchTerm(state = '', action) { + switch (action.type) { + case UPDATE_SEARCH: + return action.searchTerm; + default: + return state; + } +} + const todoApp = combineReducers({ visibilityFilter, todos, - currentTheme + currentTheme, + searchTerm }) export default todoApp