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
- 952