Videos.md 9.7 KB

Learn Redux

Video 1: Setting up Webpack

  • React Dev Tools
  • Redux Dev Tools

Video 2: App Layout + Component Setup

Video 3: Create Single and PhotoGrid components

Video 4: Setting up React Router

import { Router, Route, IndexRoute, browserHistory } from 'react-router'

const router = (
    <Router history={browserHistory}>
        <Route path='/' component={Main}>
            <IndexRoute component={} />
            <Route path='/view/:postId' />
        </Route>
    </Router>
)

render(router, document.getElementById('root'))
  • Use <Link to=''>Bla</Link> to create push state links.
  • browserHistory will be replaced later by history which first syncs the browser history with the react store (see next video).

Video 5: Setting up React Store

  • One giant state for all data
  • it is called store

    import { createStore, compose } from 'redux'
    import { syncHistoryWithStore } from 'react-router-redux'
    import { browserHistory } from 'react-router'
    
    import rootReducer from './reducers/index'
    
    const store = createStore(rootReducer, defaultState)
    export const history = syncHistoryWithStore(browserHistory, store)
    export default store
    

Video 6: Redux Actions

  • actions invoke changes of the state (adding, updating, removing data)
  • actions are objects with two elements: type and payload
  • action creators are functions which return actions:

    function increment(index) {
    return {
        type: 'INCREMENT_LIKES',
        index,
    }
    }
    function addComment(postId, author, comment) {
    return {
        type: 'ADD_COMMENT',
        postId,
        author,
        comment
    }
    }
    

Video 7: Redux Reducers

  • reducers handle the data when an action is received
  • use one reducer per components
  • reducers are functions which take
    • the action
    • a copy of the current state
  • reducers return the updated state if there is a change, or the previous state
  • always start like this and then add some conditional functionality:

    function posts(state = [], action) {
    if (...) {
        return nextState
    }
    return state
    }
    
    export default posts
    
  • combine them in a root reducer

    import { combineReducers } from 'redux'
    import { routerReducer } from 'react-router-redux'
    
    import posts from './posts'
    const rootReducer = combineReducers({ posts, ..., routing: routerReducer })
    export default rootReducer
    
  • always add routing: routerReducer at the end to integrate the route changes with other action changes.

  • executes URL changes implicitely

  • https://github.com/reactjs/react-router-redux

Video 8: Integrating our Store with React Router

  • to make the store globally available, put it on top of the routing
  • replace the browserHistory as mentioned before.

    import { Router, Route, IndexRoute, browserHistory } from 'react-router'
    +import { Provider } from 'react-redux'
    +import store, { history } from './store'
    
    const router = (
    +   <Provider store={store}>
    -       <Router history={browserHistory}>
    +       <Router history={history}>
                <Route path='/' component={Main}>
                    <IndexRoute component={} \/>
                    <Route path='/view/:postId' \/>
                <\/Route>
            <\/Router>
    +   </Provider>
    )
    
    render(router, document.getElementById('root'))
    
  • The state is now stored in the store. $r.store.getState()

Video 9: Understanding the Reducer's Job and Dispatching Actions

  • action creators return actions
  • actions invoke state changes
  • reducers change the state
  • the store provides a dispatch function

    // on the console
    $r.store.dispatch({type: 'INCREMENT', index: 1})
    
  • The dispatcher sends the action to every reducer

  • Therefore, the reducer needs to have some conditional logic.

Video 10: Accessing dispatch and state with Redux

  • in regular React, the state or parts of it are propagated via props
  • in Redux, connect injects the data in the components that need it
  • for that, we wrap the main component with one that has the state and dispatcher propagated

    import { bindActionCreators } from 'redux'
    import { connect } from 'react-redux'
    import * as actionCreators from './actions/actionCreators'
    import Main from './Main'
    
    function mapStateToProps(state) {
        return {
            posts: state.posts,
            comments: state.comments,
        }
    }
    
    function mapDispatchToProps(dispatch) {
        return bindActionCreators(actionCreators, dispatch)
    }
    
    const App = connect(mapStateToProps, mapDispatchToProps)(Main)
    export default App
    
  • because Main ist just JSX, connect injects standardized props into it and makes the state and dispatcher available.

  • You can still propagate data down with props

Video 11: Displaying Redux State inside our Components

  • Creating React components

Video 12: Updating State with Reducers

  • Once you propagate the reducer function to the component, this is how you pass arguments to the function call

    • Remember that <button onClick={this.props.function(argument)} ...> would call the function at document load!

      <button onClick={this.props.function.bind(null, argument)} ...>
      
  • Use pure functions to manipulate state. So the same inputs (previous state, action) always gives the same output (next state).

    • Makes testing easier.
    • Enables Redux dev tools, time travel etc.
  • Do the same as in React state handling: Copy, modify, return copy:

    function posts(state = [], action) {
        switch(action.type) {
            const i = action.index
            case 'INCREMENT_LIKES' :
                return [
                    ...state.slice(0, i),
                    { ...state[i], likes: state[i].likes + 1 },
                    ...state.slice(i + 1)
                ]
            default:
                return state
        }
    }
    

Video 13: Displaying the Single Photo Component

  • Polyfills are new ES6 functions cross compiled for old browsers through Babel. Example for findIndex()
  • You can use findIndex() to get a certain element of an array

    render () {
        const { postId } = this.props.params
        const i = this.props.posts.findIndex((post) => post.code === postId)
        const post = this.props.posts[i]
        const comments = this.props.comments[postId] || []
        ...
    }
    

Video 14: Displaying and Adding Comments

  • You can hide a submit button, if it's clear that the Return key submits the form.
  • consider separate render functions for looped-over elements to keep the functions clearer.

Video 15: Updating Comment State in our Store

  • Write a functions, which handles the submit of a form

    ...
    handleSubmit(e) {
    e.preventDefault()
    const { postId } = this.props.params
    const author = this.refs.author.value
    this.props.addComment(postId, author, ...)
    }
    render() {
    ...
    <form ref='commentForm' className='comment-form' onSubmit={this.handleSubmit}>
        <input type='text' ref='author' placeholder='author'/>
    ...
    }
    

Video 16: Redux Reducer Composition

  • Just like you can split up your state in different pieces, you can also split up the reducers. This is called reducer composition
  • Example for comments:

    • write one function to handle all comments
    • write other functions to only handle one comment at a time

      function postComments(state = [], action) {
      switch (action.type) {
          case 'ADD_COMMENT':
              return [
                  ...state, {
                      user: action.author,
                      text: action.comment,
                  }
              ]
          case 'REMOVE_COMMENT':
              return [
                  ...state.slice(0, action.index),
                  ...state.slice(action.index + 1)
              ]
          default:
              return state
      }
      }
      function comments(state = [], action) {
      if (typeof action.postId !== 'undefined') {
          return {
              ...state,
              [action.postId]: postComments(state[action.postId], action)
          }
      }
      return state
      }
      
      export default comment
      
    • Recall slices, spreads.

    • Think about what needs to get passed. This makes the code faster.

Video 18: Hot Reloading Redux Reducers with Webpack

  • Hot reloading only works for components etc. but not for reducer code changes.
  • if you want that, add the following code to store.js

    if (module.hot) {
    module.hot.accept('./reducers/', () => {
        const nextRootReducer = require('./reducers/index').default
        store.replaceReducer(nextRootReducer)
    })
    }
    

Video 19: Learning Redux Dev Tools

  • To make use of the Redux dev tools (for chrome), you need to announce your store to the browser:

    // index.js
    const enhancers = compose(
    window.devToolsExtension ? window.devToolsExtension() : f => f
    )
    
    const store = createStore(rootReducer, defaultState, enhancers)
    

Video 20: Wrap Up and Next Steps

  • Main idea: One giant store
  • Update it with actions
  • When actions get dispatched, they are handled by reducers
  • expose action creators and store to subcomponents
  • Can't use asynchronous functions in reducers. If you need to, there's Redux thunk and redux sagas.
  • If you have a lot of nested data comming through JSON APIs, look at normalizr
  • Look at existing sites and think about how to implement them in React/Redux.