Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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)

Radoslav Stankov

June 18, 2016
Tweet

More Decks by Radoslav Stankov

Other Decks in Technology

Transcript

  1. 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
  2. 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> ); }
  3. 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), }); }
  4. 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), }); }
  5. 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) {
  6. }); }; } 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, }); }; } }
  7. 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> ); } }
  8. 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> ); } }
  9. 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> ); } }
  10. 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> ); } }
  11. let store = { todos: [], filter: 'All', }; store

    = reduce(store, {action: 'todo/add', todo: todo}); store.todos // # => [ todo ]
  12. 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 // # => [ ]
  13. 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'
  14. 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'
  15. 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'));
  16. // 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; } }
  17. // 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; } }
  18. // reducers/index.js
 import {combineReducers} from 'redux' import todos from './todos'

    import visibilityFilter from './visibilityFilter' export default combineReducers({ todos: todos, visibilityFilter: visibilityFilter, });
  19. 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'));
  20. 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);
  21. 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);
  22. 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);
  23. 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);
  24. 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);
  25. 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);
  26. 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);
  27. 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);
  28. 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);
  29. // 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 />); }); });
  30. 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);
  31. 
 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', }; }
  32. 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