Application state management
Flux is a pattern that Facebook uses for
building front-end web applications.
It complements React's components with
a unidirectional data flow.
Flux
Flux
Motivation...
Flux
Flux
Flux
Flux
Flux is not a library
It is just a pattern
a guideline...
The race had begun
Alt
DeLorean.js
Flummox
McFly
marty.js
RefluxJS
Fluxible
material-flux
Critical Mass votes for Redux
The race was won
🎉
Redux became the industry standard
🥇
Redux Toolkit is an improvement
🥇
implementation
Single source of truth
all application state is stored as
a single JS object
store
1st Principle
The state tree is immutable
(Read-only)
You cannot write to or modify the app state directly
store
2nd Principle
To modify application state You need to dispach an Action
Actions are payloads of information
They are the only source of data of the store.
const ADD_TODO = 'ADD_TODO'
{
type: ADD_TODO,
text: 'Build my first Redux app'
}
Action Creators are functions
that create and return actions
const ADD_TODO = 'ADD_TODO'
function addTodo(text) { return { type: ADD_TODO, text: text } }
3rd Principle Changes are made with pure functions
A reducer is a pure function that takes the previous state and an action, and returns the next state.
( previousState , action ) => newState
Reducers Handle Actions
Handling Actions in Reducers
let initialState = { todos: [], visibilityFilter: 'ALL' }
function todosReducer(reducerState = initialState, action){ switch (action.type) { case SET_VISIBILITY_FILTER: return { ...reducerState, visibilityFilter: action.filter } case ADD_TODO: return { ...reducerState, todos: [...reducerState.todos,{ text: action.text, completed: false }] } default: return reducerState
}
}
Combine Reducers
reducers/index.js import { combineReducers } from 'redux' import todosReducer from './todos.reducer.js' import countReducer from './count.reducer.js'
const rootReducer = combineReducers({
todos : todosReducer,
count : countReducer
})
export default rootReducer
To create the store instance
use the redux method createStore()
createStore() requires a root reducer as 1st param
and optionally - an initialState and an enhancer
import { createStore, applyMiddleware } from 'redux'
import rootReducer from './reducers'
let store = createStore(rootReducer ,
{count:8} ,
applyMiddleware(...))
applyMiddleware(...middleware)
Middleware are functions used to extend Redux
They are composable by design
Each function requires no knowledge of what comes before or after it in the chain.
Home made Middleware
Middleware functions have the following signature:
import { createStore, applyMiddleware } from 'redux'
import rootReducer from './reducers' const logger = store => next => action => { console.log('dispatching', action) next(action) console.log('next state', store.getState() }
let store = createStore(rootReducer , {count:8} , applyMiddleware(logger) )
Home made Middleware
const logger = store => next => action => {
console.log('dispatching', action)
next(action)
console.log('next state', store.getState())
}
function logger(store) {
return function(next) {
return function(action) {
console.log('dispatching', action)
next(action)
console.log('next state', store.getState())
}
}
}
Same as writing:
3rd party Middleware
Same function signature as home-made middleware
serve as Redux 'plugins' and available as npm packages
import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import rootReducer from './reducers'
import logger from './my-middleware/logger'
let store = createStore(rootReducer , {count:8} , applyMiddleware(thunk,logger) )
regular Action Creator functions
create and return action objects
const ADD_TODO = 'ADD_TODO'
function addTodo(text) { return { type: ADD_TODO, payload: text } }
dispatching async actions with
redux-thunk middleware
Async action creators return a callback with a promise
The callback will receive the store dispatch method as a parameter
* The following code will work only if redux-thunk middleware was used at the store creation
function fetchPosts(topic) {
return async (dispatch)=> {
try{
dispatch(fetch_posts_start(topic)) // START
const url = `https://myblog.com/api/${topic}`
const response = await fetch(url) const data = await response.json() dispatch(fetch_posts_success(topic, data)) // SUCCESS
}catch(error){
dispatch(fetch_posts_failed(error)) // FAILED }
}
}
applyMiddleware(...middleware)
Middleware are functions used to extend Redux
They are composable by design
Each function requires no knowledge of what comes before or after it in the chain.
tightly coupled with React
You can write Redux apps with React, Angular, Ember, jQuery, or vanilla JavaScript.
is not
Official React bindings for Redux
<Provider store>
Injects the store to your app
hooks API
The hooks API is an elegant way to interact with the store from react
Recommended!
Connect(...)
HOC connect to store from within react component outdated,
not recommended...
<Provider store>
Wraps the root component,
Providing access to the Redux store
ReactDOM.render(
<Provider store={store}>
<MyRootComponent />
</Provider>,
document.getElementById('root')
)
hooks API
Rerender on store state changes
Affect the store state
import React from 'react'
import { useSelector, useDispatch } from 'react-redux'
export const CounterComponent = () => {
const counter = useSelector(state => state.counter)
const dispatch = useDispatch()
return (
<div>
<span>{counter}</span>
<button onClick={() => dispatch({ type: 'increment' })}>
Increment counter
</button>
</div>
)
}
Connect(...)
- Connects a React component to a Redux store.
- Components connected to a Redux store
are typically called Containers - Whenever the store will update, so will the container
import { connect } from 'react-redux'
let ProfilePage = () => {
return (
<div>
...
</div>
)
}
export default connect(...)(ProfilePage)
update the view with
mapStateToProps( state )
To use connect(), define a mapStateToProps function
It maps the parts of the store state your container cares about
into props it will pass down to its child components
import { connect } from 'react-redux'
let ListView = ({ active }) => {
return <div>...</div>
}
const mapStateToProps = (state, ownProps) => {
return {
active: ownProps.filter === state.visibilityFilter
}
}
export default connect(mapStateToProps)(ListView)
import { someActionCreator,increment,decrement,toggleTodo} from './actions'
let ListView = ({ doSomething, doIncrement, doDecrement, onTodoClick}) => {
return <div>...</div>
}
const mapDispatchToProps = (dispatch, ownProps) => {
return {
doSomething: ()=> dispatch( someActionCreator() )
doIncrement: ()=> dispatch( increment() ), doDecrement: ()=> dispatch( decrement() ),
onTodoClick: (id)=> dispatch( toggleTodo(id) )
}
}
export default connect(mapStateToProps, mapDispatchToProps)(ListView)
dispatching actions with
mapDispatchToProps( dispatch)
Optional second parameter of connect(),
It recieves dispatch() as a param and returns callback props
your container will typically inject down to its child components
* if not implemented, containers will have dispatch() available via props
Redux
By Yariv Gilad
Redux
A better way to manage application state
- 1,982