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

Integrating Redux with React

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.

Integrating Redux with React

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

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

Avatar for Radoslav Stankov

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