Redux - a critical appreciation

Abhishek Yadav

The agenda

Redux

  • React and data - props/state
  • Lifting state up
  • The Flux architecture and state-store
  • Reactivity
  • What Redux does
  • What Redux expects
  • The benefits
  • The problems

 

React and data

Redux

Props

State

The view

(DOM)

React is a library that helps keep your data in sync with the view

The Component

DOM changes when the state changes

DOM changes when the props change

React and data

Redux

Lifting state up

React applications are a designed as a tree of components

 

Each component is designed to represent a region in the UI

Lifting state up

Redux

Redux

Lifting state up

// The Votes component

class Votes extends React.Component {

   constructor(){
     this.state = {
        totalVotes: 0
     }
   }

   render() {
     //
   }
}

Lifting state up

The vote component

  • Upvote button should be green if user has already up-voted
  • Downvote button should be red if user has already down-voted

Lifting state up

The vote component - next feature

  • Upvote button should be green if user has already up-voted
  • Downvote button should be red if user has already down-voted

The vote component

Lifting state up

// The Votes component

class Votes extends React.Component {

   constructor(){
     this.state = {
        totalVotes: 0
        currentUserVote: 0; // or 1 or -1
     }
   }

   render() {
     //
   }
}
  • Add state currentUserVote to meet that requirement
  • Get its value from the server

The vote component

Lifting state up

Since there are many answers on a page, its better to load all voting data together

 

The users voting data then becomes global with respect to the voting component

 

Lifting state up

// The Votes component

class Votes extends React.Component {

   constructor(props){
     this.state = {
        totalVotes: 0
     }
   }

   render() {
     <div> 
        {props.currentUserVote}
     </div>
   }
}

Pass votingData as props to the component

 

Let the parent component deal with fetching/managing the votingData

 

We just lifted state up.

The drill down

Lifting state up

Global data gets attached to the root component, eventually - since it is used in many components

 

This data must be passed along the tree, as props, to reach where it is used

 

Functions are also passed similarly

The drill down

Lifting state up

Root level data

The drill down

Lifting state up


// App:

<div>
  <Question 
    userData={this.state.userData}
    userDataChange={this.state.handleChange}
  />
</div>


//Question:

<div>
  <Vote
     userData={this.props.userData}
     userDataChange={this.props.userDataChange}
  />
</div>

We are passing lots of props to intermediate components

The drill down

Lifting state up

Root level data

What we actually need

The store

The store

  • A data store, outside the components
    • the store
  • Components update when data changes
    • only the relevant ones
    • automatically

 

What we need

The store

  • Controlled updates for store
    • don't allow haphazard mutations
  • The update actions be pure
    • Ajax calls are cleanly separated​
  • Caching (memoizing) for large stores

 

Good to have

The data we store in the store is often called state, because it can represents the state of the UI

The store

  • Flux*
  • Redux
  • Mobx
  • Apollo-link-state

State implementations

*Flux is actually the name of the pattern. It has implementations by the same name

The Redux way

Redux

The redux way

store

component

reducers

actions

middlwares

connect

The redux way

store

component

reducers

actions

middlwares

connect

watch changes,

update props

dispatch actions

observe actions

for side effects

Mutate store

on actions.

Be pure

on props change:

update UI

initiate action

on events

keep ui state

as data

The redux way

component

connect

watch changes,

update props

dispatch actions

on props change:

update UI

initiate action

on events

The component side

  • The component should connect to Redux
  • Connect has two points : for subscription and dispatching
  • Subscription: mapsStateToProps - defines what data from the state are we watching
  • Dispatching: mapDispatchToProps - defines how we translate UI events to actions

The redux way

component

connect

watch changes,

update props

dispatch actions

on props change:

update UI

initiate action

on events

The component side - subscribing

  • The mapsStateToProps gets us the  data we need at props
  • The library updates the props whenever data changes
  • The component updates when props change (thats React behaviour)
  • We can add a componentDidUpdate to perform additional checks when props change

The redux way

component

connect

watch changes,

update props

dispatch actions

on props change:

update UI

initiate action

on events

The component side - subscribing

  • The mapsStateToProps gets us the  data we need at props
  • The library updates the props whenever data changes
  • The component updates when props change (thats React behaviour)
  • We can add a componentDidUpdate to perform additional checks when props change

const mapStateToProps = function(state){
  return {
    userVotes: state['currentUser']['votes']
  }
}

connect(mapStateToProps, null)(Votes);

The redux way

component

connect

watch changes,

update props

dispatch actions

on props change:

update UI

initiate action

on events

The component side - dispatching

  • When there is an event in the UI, we may need to update the state (like a form is filled)
  • For such scenario, we define an action that represents the event and the data.
  • We can then have the event handlers dispatch the event.
  • The library will make sure the state is updated
  • The library will also trigger any side effects if needed

The library = react-redux

The redux way

component

connect

watch changes,

update props

dispatch actions

on props change:

update UI

initiate action

on events

The component side - dispatching

  • When there is an event in the UI, we may need to update the state (like a form is filled)
  • For such scenario, we define an action that represents the event and the data.
  • We can then have the event handlers dispatch the event.
  • The library will make sure the state is updated
  • The library will also trigger any side effects if needed

The library = react-redux

// action.js

function upVote(upOrDown) {
  return {
    type: UPVOTE,
    upOrDown,
  }
}


// Vote Component
handleUpVote(e) {
  e.preventDefault();
  this.props.dispatch(upVote(true))
}


// Connect
const mapDispatchToProps(dispatch) {
    return dispatch;
}

connect(mapStateToProps, mapDispatchToProps)(Vote);

The redux way

The component side - dispatching

  • The action upVote contains the voting date (up or down)
  • handleUpVote is a usual React event handler. Instead of doing things itself, it dispatches the action.
  • Dispatch is done using a special dispatch function (comes from Redux)
  • The mapDispatchToProps here makes dispatch available as a prop.
// action.js

function upVote(upOrDown) {
  return {
    type: UPVOTE,
    upOrDown,
  }
}

// Vote Component
handleUpVote(e) {
  e.preventDefault();
  this.props.dispatch(countVote(true))
}


// Connect
const mapDispatchToProps(dispatch) {
    return dispatch;
}

connect(
    mapStateToProps, 
    mapDispatchToProps
)(Vote);

The redux way

store

reducers

actions

Mutate store

on actions.

Be pure

keep ui state

as data

The store side

  • The reducer makes changes to the data in store
  • It is invoked on every action
  • We write reducers
  • It has t be pure

The redux way

The store side - reducer


function voteDataReducer(state, action) {
  switch (action.type) {
    case UPVOTE:
      return Object.assign({}, state, 
        { userVote: userVote + action.upOrDown });
    default:
      return state;
  }
}
  • The reducer makes changes to the data in store
  • It is invoked on every action
  • We write reducers
  • It has t be pure

The redux way

actions

middlwares

observe actions

for side effects

  • The middleware watches for actions and performs side effects when needed
  • Things like Ajax calls, localStorage changes etc can go here
  • Usually via libraries like redux-saga or redux-thunk

The middleware

The redux way

actions

middlwares

observe actions

for side effects

The middleware

//

The redux way

The action


// App:
  • The action
  •  

The redux way

The problems

  • The problems
  •  

ROOT

___|___

/   / \   \

a  b  c  d

 

root

a

b

c

d

x

y

m

n

The component

State

props

Root level data

Components that need the same data

component data

(state)

Data passed as props

Common parent component

Components that need the same data

state

Data passed as props

Common parent component

store

component

reducers

actions

middlwares

connect

The redux way

store

component

reducers

actions

middlwares

connect

watch changes,

update props

dispatch actions

observe actions

for side effects

Mutate store

on actions.

Be pure

on props change:

update UI

initiate action

on events

keep ui state

as data

Redux

store

component

a

lot

of

other stuff

component

connect

watch changes,

update props

dispatch actions

on props change:

update UI

initiate action

on events

actions

middlwares

observe actions

for side effects

store

reducers

actions

Mutate store

on actions.

Be pure

keep ui state

as data

Redux - a critical appreciation

By Abhishek Yadav

Redux - a critical appreciation

  • 869