Repozytorium Web Developera

Archiwum z lat 2013-2018, treści mogą być nieaktualne.

React Redux

Useful links

Learning path about Redux/Flux:

  1. Getting Started with Redux: An Intro
  2. Egghead Getting Started with Redux
  3. Why use Redux over Facebook Flux?
  4. Redux project architecture
  5. React Redux style guide

Redux basics

  • action - describes an action and calls a reducer
  • reducer - modifies state
  • store - updates view
  • view - triggers actions

Action

Actions defines

This exemplary action has its unique type, which is defined above. Then other parameters are being passed, and moved further to the reducer when the action is called:


export function submit (email, name) {
  return {
    type: SUBMIT,
    payload: { email, name }
  }
}

So we define the actions that represent the facts about “what happened” and the reducers that update the state according to those actions.

Reducer

It takes the previous state and action being dispatched and returns the next state of your application. It must be a pure function which doesn't mutate it's arguments and it's predictable.


const initialState = {
  isLoading: false,
  isSent: false
}

function simpleReducer (state = initialState, action) {
  switch (action.type) {
    case SUBMIT:
      return Object.assign({}, state, {
        isLoading: true
      })
    case SUBMIT_SUCCESS:
      return Object.assign({}, state, {
        isLoading: false,
        isSent: true
      })
    default:
      return state
  }
}

Sources:

Store

There should be only a single store in your app. Store consists of many reducers which modify the slice of store state it manages. Store is an object that holds the complete state of your app. The only way to change its state is by dispatching actions. You may also subscribe to the changes to its state to update the UI.

You can't modify or read state directly. You have to use getState to read or reducers to modify which are called by dispatching an action dispatch(action).

createStore(reducer, [preloadedState], [enhancer])

Redux Methods

  • applyMiddleware (while creating a store)
  • compose (?)

Redux advanced tools

Redux Thunks

What is redux-thunk

Redux Thunk middleware allows you to write action creators that return a function instead of an action. The thunk can be used to delay the dispatch of an action, or to dispatch only if a certain condition is met. The inner function receives the store methods dispatch and getState as parameters.

For example, redux-thunk lets the action creators invert control by dispatching functions. They would receive dispatch as an argument and may call it asynchronously. Such functions are called thunks.


// Meet thunks.
// A thunk is a function that returns a function.
// This is a thunk.
function makeASandwichWithSecretSauce(forPerson) {
 
  // Invert control!
  // Return a function that accepts `dispatch` so we can dispatch later.
  // Thunk middleware knows how to turn thunk async actions into actions.
  return function (dispatch) {
    return fetchSecretSauce().then(
      sauce => dispatch(makeASandwich(forPerson, sauce)),
      error => dispatch(apologize('The Sandwich Shop', forPerson, error))
    )
  }
}

Action Creators

Actions creators has getState which gets values from store. It should get parameters, not whole getState.

Redux Promises

Another example of middleware is redux-promise. It lets you dispatch a Promise async action, and dispatches a normal action when the Promise resolves.

Reselect - selectors

mapStateToProps is where you should use `reselect` to create selectors, ie:


import { createSelector } from 'reselect'
const counter = (state) => state.counter
const tripleCount = createSelector(counter, (count) => count * 3)
const mapStateToProps = (state) => ({
  counter: tripleCount(state)
})

Selectors can compute derived data, allowing Redux to store the minimal possible state.

Selectors are efficient. A selector is not recomputed unless one of its arguments change.

Selectors are composable. They can be used as input to other selectors.

reselect github

Redux Forms

React component with a form is connected to Redux Store thankfully to formReducer function.

Redux app architecture

Routes, components, containers

A route consists of a feature which has its own components and container. Container connects a main feature component with a state from store: connect(mapStateToProps, mapActionCreators)(Component). It's similar to inject store in MobX to a component.

mapStateToProps gets state from a global store and specifies how the props should look in the component. An example:


import { connect } from 'react-redux'
import { getFormValues } from 'redux-form'
import AdminAccountSetup from '../components/AdminAccountSetup'

...

const mapStateToProps = state => ({
  user: state.user,
  formValues: getFormValues('admin_account_setup')(state),
})

export default connect(mapStateToProps, mapActionCreators)(AdminAccountSetup)

mapActionCreators is an object of action creators and it is specified below:


import { submitGetOrderDetail } from 'modules/orderDetail/orderDetail'

const mapActionCreators = {
  submitGetOrderDetail,
}

So basically components gets props passed to it by a container. These props can be obtained from store or from actions modules.

Container components does not contain any JSX, nor does it import React. This component is **only** responsible for wiring in the actions and state necessary to render a presentational component.

Best practices

Pass as little data as possible in your actions.

Keep your state object flat.

Actions

Use action creators to dispatch actions instead of assembling and emitting them directly from your views.

Reducers

Your root reducer should be composed of smaller reducers that manage specific parts of the application state.

Where to put API calls?

Use React for ephemeral state that doesn't matter to the app globally and doesn't mutate in complex ways. For example, a toggle in some UI element, a form input state. Use Redux for state that matters globally or is mutated in complex ways. For example, cached users, or a post draft. Sometimes you'll want to move from Redux state to React state (when storing something in Redux gets awkward) or the other way around (when more components need to have access to some state that used to be local). The rule of thumb is: do whatever is less awkward.

  • If a component handles information not required by any other components, use React state. It is often the case for information related to UI state.
  • If you need to share a bundle of data through components, prefer Redux to mutualise information.
Source.

How to create Redux modules

Ducks


// widgets.js

// Actions
const LOAD   = 'my-app/widgets/LOAD';
const CREATE = 'my-app/widgets/CREATE';
const UPDATE = 'my-app/widgets/UPDATE';
const REMOVE = 'my-app/widgets/REMOVE';

// Reducer
export default function reducer(state = {}, action = {}) {
  switch (action.type) {
    // do reducer stuff
    default: return state;
  }
}

// Action Creators
export function loadWidgets() {
  return { type: LOAD };
}

export function createWidget(widget) {
  return { type: CREATE, widget };
}

export function updateWidget(widget) {
  return { type: UPDATE, widget };
}

export function removeWidget(widget) {
  return { type: REMOVE, widget };
}

// side effects, only as applicable
// e.g. thunks, epics, etc
export function getWidget () {
  return dispatch => get('/widget').then(widget => dispatch(updateWidget(widget)))
}
  1. MUST export default a function called reducer()
  2. MUST export its action creators as functions
  3. MUST have action types in the form npm-module-or-app/reducer/ACTION_TYPE
  4. MAY export its action types as UPPER_SNAKE_CASE, if an external reducer needs to listen for them, or if it is a published reusable library

More information on Github

Code splitting and Redux Registry

More information about REdux Modules and Code Splitting