Integrating Redux with React

Integrating Redux with React

Presentations walks through the steps of integrating redux in react project.

(Video: https://www.youtube.com/watch?v=3x6qGs1_aoU)

7a0e72a6f55811246bb5d9a946fd2e49?s=128

Radoslav Stankov

June 18, 2016
Tweet

Transcript

  1. 3.
  2. 4.
  3. 6.
  4. 7.
  5. 8.
  6. 9.

    const FILTERS = { All: () => true, Active: (t)

    => !t.completed, Completed: (t) => t.completed, }; export default class App extends React.Component { constructor(props) { super(props); this.state = { filter: 'All', todos: [], }; } render() { return ( <div className="app"> <div className="arrow"> <Input onSave={this.handleNewTodo.bind(this)} /> <ul className="list"> {this.filteredTodos().map((todo) => ( <li key={todo.id} className={todo.completed ? 'completed' : ''}> <input className="toggle" type="checkbox" checked={todo.complete
  7. 10.

    render() { return ( <div className="app"> <div className="arrow"> <Input onSave={this.handleNewTodo.bind(this)}

    /> <ul className="list"> {this.filteredTodos().map((todo) => ( <li key={todo.id} className={todo.completed ? 'completed' : ''}> <input className="toggle" type="checkbox" checked={todo.complete <label>{todo.text}</label> <button className="destroy" onClick={this.removeTodoHandler(todo </li> ))} </ul> <div className="footer"> <div className="counter">{this.counterText()}</div> {Object.keys(FILTERS).map((filterName) => ( <button className={filterName === this.state.filter ? 'selected' : ))} <button className="clear" style={{visibility: this.showCompletedCoun </div> </div> ); }
  8. 11.

    counterText() { const count = this.state.todos.filter((t) => !t.completed).length; return `${

    count } ${ count === 1 ? 'item' : 'items' } left`; } filteredTodos() { return this.state.todos.filter(FILTERS[this.state.filter]); } showCompletedCount() { return this.todos.filter((t) => t.completed).length > 0; } handleNewTodo(text) { const todo = { id: +(new Date()), text: text, completed: false }; this.setState({ todos: [todo].concat(this.state.todos), }); }
  9. 12.

    filteredTodos() { return this.state.todos.filter(FILTERS[this.state.filter]); } showCompletedCount() { return this.todos.filter((t) =>

    t.completed).length > 0; } handleNewTodo(text) { const todo = { id: +(new Date()), text: text, completed: false }; this.setState({ todos: [todo].concat(this.state.todos), }); } handleClearCompleted() { this.setState({ todos: this.state.todos.filter((t) => !t.completed), }); }
  10. 13.

    this.setState({ todos: [todo].concat(this.state.todos), }); } handleClearCompleted() { this.setState({ todos: this.state.todos.filter((t)

    => !t.completed), }); } removeTodoHandler(todo) { return (e) => { this.setState({ todos: this.state.todos.filter((t) => t.id !== todo.id), }); }; } toggleTodoHandler(todo) { return (e) => { this.setState({ todos: this.state.todos.map((t) => { if (t.id === todo.id) {
  11. 14.

    }); }; } toggleTodoHandler(todo) { return (e) => { this.setState({

    todos: this.state.todos.map((t) => { if (t.id === todo.id) { t.completed = !t.completed; } return t; }), }); }; } filterHander(filterName) { return (e) => { this.setState({ filter: filterName, }); }; } }
  12. 15.
  13. 16.
  14. 27.

    export default class App extends React.Component { // . .

    . render() { return ( <div className="app"> <NewTodo addTodo={this.addTodo} /> <TodoList todos={this.state.todos.filter(FILTERS[this.state.filter])} toggleTodo={this.toggleTodo} removeTodo={this.removeTodo} /> <Footer todos={this.state.todos} filters={FILTERS} activeFilter={this.state.filter} changeFilter={this.changeFilter} clearCompletedTodos={this.clearCompletedTodos} /> </div> ); } }
  15. 28.

    export default class TodoList extends React.Component { static propTypes =

    { toggleTodo: React.PropTypes.func.isRequired, removeTodo: React.PropTypes.func.isRequired, todos: React.PropTypes.array.isRequired, }; render() { return ( <ul className="list"> {this.props.todos.map((todo) => ( <TodoItem key={todo.id} todo={todo} toggleTodo={this.props.updateTodo} removeTodo={this.props.removeTodo} /> ))} </ul> ); } }
  16. 29.
  17. 32.

    export default class App extends React.Component { // . .

    . render() { return ( <div className="app"> <NewTodo addTodo={this.addTodo} /> <TodoList todos={this.state.todos.filter(FILTERS[this.state.filter])} toggleTodo={this.toggleTodo}
 updateTodo={this.updateTodo} removeTodo={this.removeTodo} /> <Footer todos={this.state.todos} filters={FILTERS} activeFilter={this.state.filter} changeFilter={this.changeFilter} clearCompletedTodos={this.clearCompletedTodos} /> </div> ); } }
  18. 33.

    export default class TodoList extends React.Component { static propTypes =

    { toggleTodo: React.PropTypes.func.isRequired, removeTodo: React.PropTypes.func.isRequired, updateTodo: React.PropTypes.func.isRequired, todos: React.PropTypes.array.isRequired, }; render() { return ( <ul className="list"> {this.props.todos.map((todo) => ( <TodoItem key={todo.id} todo={todo} toggleTodo={this.props.updateTodo} toggleTodo={this.props.updateTodo} removeTodo={this.props.removeTodo} /> ))} </ul> ); } }
  19. 34.
  20. 35.
  21. 36.
  22. 42.

    let store = { todos: [], filter: 'All', }; store

    = reduce(store, {action: 'todo/add', todo: todo}); store.todos // # => [ todo ]
  23. 43.

    let store = { todos: [], filter: 'All', }; store

    = reduce(store, {action: 'todo/add', todo: todo}); store.todos // # => [ todo ] store = reduce(store, {action: 'todo/toggle', id: todo.id}); store = reduce(store, {action: 'todo/clear', id: todo.id}); store.todos // # => [ ]
  24. 44.

    let store = { todos: [], filter: 'All', }; store

    = reduce(store, {action: 'todo/add', todo: todo}); store.todos // # => [ todo ] store = reduce(store, {action: 'todo/toggle', id: todo.id}); store = reduce(store, {action: 'todo/clear', id: todo.id}); store.todos // # => [ ] store = reduce(store, {action: 'filter/select', name: 'Active'}); store.filter // # => 'Active'
  25. 45.

    import {createStore} from 'redux'
 let store = createStore(reduce) store.dispatch({action: 'todo/add',

    todo: todo}); store.getState().todos // # => [ todo ] store.dispatch({action: 'todo/toggle', id: todo.id}); store.dispatch({action: 'todo/clear', id: todo.id}); store.getState().todos // # => [ ] store.dispatch({action: 'filter/select', name: 'Active'}); store.getState().filter // # => 'Active'
  26. 47.

    import React from 'react' import {render} from 'react-dom' import {Provider}

    from 'react-redux' import {createStore} from 'redux' import reducer from './reducers' import App from './components/App' let store = createStore(reducer) let root = ( <Provider store={store}> <App /> </Provider> ); render(root, document.getElementById('root'));
  27. 54.

    // reducers/todos.js export default function todoReducer(state = [], action) {

    switch (action.type) { case 'todo/add': return [action.todo].concat(state); case 'todo/toggle': return state.map((t) => t.id === action.id ? { ...t, completed: t case 'todo/remove': return state.filter((t) => t.id !== action.id); case 'todo/clear': return state.filter((t) => !t.completed) default: return state; } }
  28. 55.

    // reducers/visibilityFilter.js export default function filterReducer(state, action) { if (!state)

    { state = DEFAULT_STATE; } switch (action.type) { case 'filter/select': const name = action.filterName; const filter = FILTERS[name]; if (!filter) { return state; } return { name: name, filter: filter }; default: return state; } }
  29. 56.

    // reducers/index.js
 import {combineReducers} from 'redux' import todos from './todos'

    import visibilityFilter from './visibilityFilter' export default combineReducers({ todos: todos, visibilityFilter: visibilityFilter, });
  30. 57.

    import React from 'react' import {render} from 'react-dom' import {Provider}

    from 'react-redux' import {createStore} from 'redux' import reducer from './reducers' import App from './components/App' let store = createStore(reducer) let root = ( <Provider store={store}> <App /> </Provider> ); render(root, document.getElementById('root'));
  31. 60.

    import {connect} from 'react-redux' class TodoList extends React.Component { render()

    { const todos = this.props.todos.filter(this.props.filter); return ( <ul className="list"> {todos.map((todo) => <TodoItem key={todo.id} todo={todo} />)} </ul> ); } } const decorate = connect((state) => { return { todos: state.todos, filter: state.visibilityFilter.filter, }; }); export default decorate(TodoList);
  32. 61.

    import {connect} from 'react-redux' class TodoList extends React.Component { render()

    { const todos = this.props.todos.filter(this.props.filter); return ( <ul className="list"> {todos.map((todo) => <TodoItem key={todo.id} todo={todo} />)} </ul> ); } } const decorate = connect((state) => { return { todos: state.todos, filter: state.visibilityFilter.filter, }; }); export default decorate(TodoList);
  33. 62.

    import {connect} from 'react-redux' class TodoList extends React.Component { render()

    { const todos = this.props.todos.filter(this.props.filter); return ( <ul className="list"> {todos.map((todo) => <TodoItem key={todo.id} todo={todo} />)} </ul> ); } } const decorate = connect((state) => { return { todos: state.todos, filter: state.visibilityFilter.filter, }; }); export default decorate(TodoList);
  34. 63.

    import {connect} from 'react-redux' class TodoList extends React.Component { render()

    { const todos = this.props.todos.filter(this.props.filter); return ( <ul className="list"> {todos.map((todo) => <TodoItem key={todo.id} todo={todo} />)} </ul> ); } } const decorate = connect((state) => { return { todos: state.todos, filter: state.visibilityFilter.filter, }; }); export default decorate(TodoList);
  35. 64.

    import {connect} from 'react-redux' class TodoList extends React.Component { render()

    { const todos = this.props.todos.filter(this.props.filter); return ( <ul className="list"> {todos.map((todo) => <TodoItem key={todo.id} todo={todo} />)} </ul> ); } } const decorate = connect((state) => { return { todos: state.todos, filter: state.visibilityFilter.filter, }; }); export default decorate(TodoList);
  36. 65.

    import {connect} from 'react-redux' class TodoList extends React.Component { render()

    { const todos = this.props.todos.filter(this.props.filter); return ( <ul className="list"> {todos.map((todo) => <TodoItem key={todo.id} todo={todo} />)} </ul> ); } } const decorate = connect((state) => { return { todos: state.todos, filter: state.visibilityFilter.filter, }; }); export default decorate(TodoList);
  37. 66.

    import {connect} from 'react-redux' class TodoList extends React.Component { render()

    { const todos = this.props.todos.filter(this.props.filter); return ( <ul className="list"> {todos.map((todo) => <TodoItem key={todo.id} todo={todo} />)} </ul> ); } } const decorate = connect((state) => { return { todos: state.todos, filter: state.visibilityFilter.filter, }; }); export default decorate(TodoList);
  38. 68.

    import {connect} from 'react-redux' export class TodoList extends React.Component {

    render() { const todos = this.props.todos.filter(this.props.filter); return ( <ul className="list"> {todos.map((todo) => <TodoItem key={todo.id} todo={todo} />)} </ul> ); } } const decorate = connect((state) => { return { todos: state.todos, filter: state.visibilityFilter.filter, }; }); export default decorate(TodoList);
  39. 69.

    import {connect} from 'react-redux' export class TodoList extends React.Component {

    render() { const todos = this.props.todos.filter(this.props.filter); return ( <ul className="list"> {todos.map((todo) => <TodoItem key={todo.id} todo={todo} />)} </ul> ); } } const decorate = connect((state) => { return { todos: state.todos, filter: state.visibilityFilter.filter, }; }); export default decorate(TodoList);
  40. 70.

    // Using: chai, enzyme, chai-enzyme import {TodoList} from 'components/TodoList'; import

    {TodoItem} from 'components/TodoItem'; describe(TodoList.name, () => { const all = () => true const none = () => false it("renders list of TodoItem(s)", () => { const todos = [fakeTodo()];
 const element = shallow(<TodoList todos={todos} filter={all} />); 
 expect(element).to.have.descendants(<TodoItem />); }); it("filters with filter", () => { const todos = [fakeTodo()]; const element = shallow(<TodoList todos={todos} filter={none} />); expect(element).to.have.descendants(<TodoItem />); }); });
  41. 72.

    export class TodoItem extends React.Component { static propTypes = {

    todo: React.PropTypes.object.isRequired, dispatch: React.PropTypes.func.isRequired, }; handleRemove() { this.props.dispatch(removeTodo(this.props.todo)); } handleToggle() { this.props.dispatch(toggleTodo(this.props.todo)); } render() { return ( <li className={this.props.todo.completed ? 'completed' : ''}> <CheckBox checked={this.props.todo.completed} onClick={this.handleTogg <label>{this.props.todo.text}</label> <DeleteButton onClick={this.handleRemove.bind(this)} /> </li> ); } } export default connect()(TodoItem);
  42. 73.

    
 export function toggleTodo(todo) { return { type: 'todo/toggle', id:

    todo.id, }; } export function removeTodo(todo) { return { type: 'todo/remove', id: todo.id, }; } export function clearCompletedTodos() { return { type: 'todo/clear', }; }
  43. 74.
  44. 75.

    Redux friends • selectors - https://github.com/reactjs/reselect • async - https://github.com/gaearon/redux-thunk

    • router - https://github.com/reactjs/react-router-redux • data fetching - https://github.com/relax/relate • store - https://github.com/elgerlambert/redux-localstorage • logger - https://github.com/evgenyrodionov/redux-logger
  45. 78.
  46. 80.