Redux or Not Redux
Gergel Gorvat
Volodymyr Vyshko
JS Community
Gergel Gorvat
Volodymyr Vyshko
JS Community
Agenda
Intro
Why you should use Redux
Why you shouldn't
Overview
State management
JavaScript community
In general
User
Scene
Router
State
What is the state?
State
Allocated objects
All the variables
File descriptors
Network sockets
It is basically all of the information that represents what is currently happening in the application.
What is the state management?
What is your opinion?
How data gets into/out of a store
How business logic interacts with the UI
Application architecture
How information flows through a system
Data persistence
Programming paradigms
networking and caching
Presentation behavior
When would I need to use a state management tool?
You have reasonable amounts of data changing over time
Many people working on the project, you want a consistent way to work with state
You don't want to reinvent the wheel again by your own
You want to use some extensions for easier debugging
56.4k
Why should I use Redux?
TL;DR
Is Redux dead, dying, deprecated, or about to be replaced?
No.
Hi, I'm a Redux maintainer.
No, Context does not replace Redux, although there is some overlap. And yes, Redux will continue to be very relevant and useful for a long time. It's currently used by 50% of React apps, and lots of folks are continuing to learn it on a daily basis.
Mark Erikson
Redux good points
Consistent architectural pattern
Debugging capabilities
Middleware
Addons and extensibility
Cross-platform and cross-framework usage
Better performance than Conext
Redux = proven by time, gives you a pattern
Still good in many cases
Not a silver bullet, but
Boilerplate problem
Boilerplate problem
Boilerplate problem
Boilerplate problem
Boilerplate problem
Boilerplate problem
Boilerplate problem
const types = {
INCREMENT: 'counter/redux/increment',
DECREMENT: 'counter/redux/decrement',
RESET: 'counter/redux/reset',
SET_STEP: 'counter/redux/setStep',
};
export default types;
import types from './types';
export const setStep = (payload) => ({
type: types.SET_STEP,
payload,
});
export const increment = (payload) => ({
type: types.INCREMENT,
payload,
});
export const decrement = (payload) => ({
type: types.DECREMENT,
payload,
});
export const reset = () => ({
type: types.RESET,
});
import { combineReducers } from 'redux';
import types from './types';
const initialState = {
step: 10,
value: 0,
};
const step = (state = initialState.step, action) => {
switch (action.type) {
case types.SET_STEP:
return action.payload;
default:
return state;
}
};
const value = (state = initialState.value, action) => {
switch (action.type) {
case types.INCREMENT:
return state + action.payload;
case types.DECREMENT:
return state - action.payload;
case types.RESET:
return initialState.value;
default:
return state;
}
};
export default combineReducers({ step, value });
How to get rid of the boilerplate?
¯\_(ツ)_/¯
The solution
Redux Toolkit
From the Redux Team
https://redux-toolkit.js.org
import { createSlice } from '@reduxjs/toolkit';
const initialState = {
step: 10,
value: 0,
};
export const counterSlice = createSlice({
name: 'counter/toolkit',
initialState,
reducers: {
increment: (state, action) => {
state.value += action.payload;
},
decrement: (state, action) => {
state.value -= action.payload;
},
setStep: (state, action) => {
state.step = action.payload;
},
},
});
export const {
increment,
decrement,
setStep
} = counterSlice.actions;
export default counterSlice.reducer;
thunk / createAsyncThunk
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'
import { userAPI } from './userAPI'
// First, create the thunk action
const fetchUserById = createAsyncThunk(
'users/fetchByIdStatus',
async (userId, thunkAPI) => {
const response = await userAPI.fetchById(userId)
return response.data
}
)
// Then, handle actions in your reducers:
const usersSlice = createSlice({
name: 'users',
initialState: { entities: [], loading: 'idle' },
reducers: {
// standard reducer logic, with auto-generated action types per reducer
},
extraReducers: (builder) => {
// Add reducers for additional action types here, and handle loading state as needed
builder.addCase(fetchUserById.fulfilled, (state, action) => {
// Add user to the state array
state.entities.push(action.payload)
})
},
})
// Later, dispatch the thunk as needed in the app
dispatch(fetchUserById(123)
- pending: 'users/requestStatus/pending'
- fulfilled: 'users/requestStatus/fulfilled'
- rejected: 'users/requestStatus/rejected'
What else?
store.js
import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import reducer from './rootReducer';
const composeEnhancers =
(process.env.NODE_ENV === 'development' &&
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) ||
compose;
const middleware = [thunk];
export const store = createStore(
reducer,
/* preloadedState, */
composeEnhancers(applyMiddleware(...middleware)),
);
import { configureStore } from '@reduxjs/toolkit';
import reducer from './rootReducer';
export const store = configureStore({
reducer,
devTools: process.env.NODE_ENV === 'development', // true by default
});
Or Not Redux?
My experience with Redux
function mapStateToProps(state) {
return {
aRandomProp: Math.random()
}
}
function mapStateToProps(state) {
return {
aFilteredArray: state.anArray.filter(value => value.isTrue === true)
}
}
Creating New Objects In mapStateToProps
Use selectors, memoization and global state instead
Redux nested containers
Container
Container
Container
Component
Container
1..N
1..N
Component
Component
Component
Redux State Duplication
const state = {
users: [{ ... }],
selectedUser: {
id: 1,
name: 'Alyosha',
start: startTime,
end: endTime
},
numberOfUsers: 10,
}
Often we have a piece of the state which can be computed from another
React state
Redux
(link)
Redux doesn't solve any problem. It just creates a new problem by giving you an abstract box. And what to store is that box - its your problem.
Dan Abramov
An antipattern is just like a pattern, except that instead of a solution it gives something that looks superficially like a solution but isn't one.
Andrew Koenig (C Traps and Pitfalls)
No encapsulation
Different lifecycle
Global store is a singleton
Problems with this global store
variables visible to all the other components, even components that don’t need to know about this prop
We must manually keep cleaning the store from garbage of old components that already left the screen
but a component may have many instances, singletons are most of the time considered an anti-pattern
Don't use Redux if you wasting much CPU time or RAM resources on maintaining the immutability
Don't use redux if your application using it mainly for caching fetched resources
Don't use redux if your app mainly consists of complex forms
Don't use Redux if your containers are mostly independent
Redux Google trend worldwide for the last year
October 2019
October 2020
25%
50%
75%
100%
25%
Why the popularity has decreased?
Learning curve
Boilerplate code
Performance
Central store
No built-in way to handle side-effects
State of JS 2020
Should we stop using Redux?
Is it dead ?
Use react features/hooks instead ?
Use Redux when
You need a single source of truth
You want to maintain an undo history
You need a serializable state/actions
Travel between the state history in development
Provide alternative UI without disturbing too much of the business logic
Otherwise...
Which library to use INSTEAD??
Small projects, without a reasonable amount of data, that is changing over time
Built-in features into your view lib/framework
Mostly keeping GraphQL data
Apollo
Relay
Medium-big projects with wide model and many async updates
MobX
Big projects with long support and big teams and with good predictability
Redux
MST
Projects with predictable model transitions and good reliability
JS Community
Questions
JS Community
Quiz time!
Join http://kahoot.it/ or use the QR code!
Thank You
JS Community
Redux
By Gergely Horváth
Redux
- 129