Previously, we talked about some of the major terms or features of React which may go unnoticed as we build or work on more and more project with the awesome UI library.
As we make more and more big or complex applications with React, we come to know that handling the overall state of the entire app isn't possible with just React.Component
class, using a constructor()
with setState()
call. We need a state container like the famous Redux
to run it in different environments, centralizing the app state/logic and is easily debuggable.
So just like before, here are some more terms or features of Redux which you might have used but didn't quite know what or how exactly they do...and yes, with examples!
1. What are the core principles of Redux? (Doc 📃) X﹏X
Redux works on three core principles:
1️⃣ Single source of truth
This means that the state of your entire application is stored in a store as an object tree.
It has some advantages like:
- A single state tree makes it easier to debug or inspect an application.
- It also enables you to persist your app's state in development, for a faster development cycle.
- This makes it easy to create universal or large-scale apps.
Example:
// If you execute the following:
console.log(store.getState())
// You'll get this as a result:
{
visibilityFilter: 'SHOW_ALL',
todos: [
{
text: 'Consider using Redux',
completed: true,
},
{
text: 'Keep all state in a single tree',
completed: false
}
]
}
2️⃣ State is read-only
The only way you change the state is by releasing an action.
Because all changes are centralized and happen one by one, there are no subtle race conditions to watch out for.
Example:
// This is how you dispatch actions:
store.dispatch({
type: 'COMPLETE_TODO',
index: 1
})
store.dispatch({
type: 'SET_VISIBILITY_FILTER',
filter: 'SHOW_COMPLETED'
})
3️⃣ Changes are made with pure functions
You write reducers to specify how the state is transformed by actions.
Because reducers are just (pure) functions, you can control the order in which they are called, pass additional data, or even make reusable reducers.
Example:
// Here's how to create pure functions/reducers
function visibilityFilter(state = 'SHOW_ALL', action) {
switch (action.type) {
case 'SET_VISIBILITY_FILTER':
return action.filter
default:
return state
}
}
function todos(state = [], action) {
switch (action.type) {
case 'ADD_TODO':
return [
...state,
{
text: action.text,
completed: false
}
]
case 'COMPLETE_TODO':
return state.map((todo, index) => {
if (index === action.index) {
return Object.assign({}, todo, {
completed: true
})
}
return todo
})
default:
return state
}
}
import { combineReducers, createStore } from 'redux'
const reducer = combineReducers({ visibilityFilter, todos })
const store = createStore(reducer)
2. How's Flux better when compared to Redux? (Flux Doc 📃) (。﹏。*)
Flux is a pattern and Redux is a library. But there are few compromises you need to make when using Redux:
You will need to avoid mutations: You can enforce this with packages like
redux-immutable-state-invariant
, Immutable.js etc.You're going to have to carefully pick your packages: Redux has extension points such as middleware and store enhancers, having a vast ecosystem of packages.
There is no nice Flow integration yet.
Here's a great article for those who want to know more about Flux and Redux:
3. What is the difference between mapStateToProps()
and mapDispatchToProps()
? ┗( T﹏T )┛
> mapStateToProps()
(Doc 📃)
It is a utility which helps your component to get updated state (which is updated by some other components). This function should be passed as the first argument to connect
and will be called every time when the Redux store state changes.
Example:
// TodoList.js
function mapStateToProps(state) {
const { todos } = state
return { todoList: todos.allIds }
}
export default connect(mapStateToProps)(TodoList)
> mapDispatchToProps()
(Doc 📃)
It is a utility which will help your component to fire an action event (dispatching action which may cause a change of application state). The component gets the [dispatch](https://react-redux.js.org/api/connect#dispatch)
by default.
Example:
import { addTodo, deleteTodo, toggleTodo } from './actionCreators'
const mapDispatchToProps = {
addTodo,
deleteTodo,
toggleTodo
}
export default connect(
null,
mapDispatchToProps
)(TodoApp)
4. What if you dispatch an action in the reducer? >_<
This comes back from a popular Stackoverflow question embedded below. Always remember that:
Dispatching an action within a reducer is an anti-pattern.
is it possible to dispatch an action in a reducer itself? I have a progressbar and an audio element. The goal is to update the progressbar when the time gets updated in the audio element. But I don't know where to place the ontimeupdate eventhandler, or how to dispatch an…
5. What about dispatching and action on load? <( _ _ )>
Yes yes, you can dispatch an action in on load with componentDidMount()
method verifying the data in the render()
method.
Example:
class App extends Component {
componentDidMount() {
this.props.fetchData()
}
render() {
return this.props.isLoaded
? <div>{'Loaded'}</div>
: <div>{'Not Loaded'}</div>
}
}
const mapStateToProps = (state) => ({
isLoaded: state.isLoaded
})
const mapDispatchToProps = { fetchData }
export default connect(mapStateToProps, mapDispatchToProps)(App)
6. How to reset state in Redux? (≧﹏ ≦)
The best way is to use a root reducer and handle the action to reducer generated by combineReducers()
. If you want to know deeply how exactly it's done check out this article.
Example:
const appReducer = combineReducers({
// Top-level reducers here
})
const rootReducer = (state, action) => {
if (action.type === 'USER_LOGOUT') {
state = undefined
}
return appReducer(state, action)
}
7. Why we use the at (@) symbol in the Redux connect decorator? (≧﹏ ≦)
In JavaScript, the @ symbol is used to signify decorators. Learn more about them here. Basically you annotate or modify classes and properties with them.
I am learning Redux with React and stumbled upon this code. I am not sure if it is Redux specific or not, but I have seen the following code snippet in one of the examples.
@connect((state) => {
return {
key: state.a.b
};
})
While the functionality of connect
is…
8. What is the difference between React context
and React-Redux? (T_T)
> React context
(Doc 📃)
Context provides a way to pass data through the component tree without having to pass props down manually at every level.
Consider using it for a small-scale application whereas Redux provides this and much more deep and powerful state control internally.
> React-Redux (Doc 📃)
React-Redux is the official React binding for Redux. It lets your React components read data from a Redux store, and dispatch actions to the store to update data.
9. How to make an AJAX request in Redux? (T_T)
AJAX allows you to send asynchronous HTTP requests to submit or retrieve data from the server.
To do an AJAX call in Redux you can use the redux-thunk
middleware which allows you to define async actions.
Example:
export function fetchAccount(id) {
return dispatch => {
dispatch(setLoadingAccountState()) // Show a loading spinner
fetch(`/account/${id}`, (response) => {
dispatch(doneFetchingAccount()) // Hide loading spinner
if (response.status === 200) {
dispatch(setAccount(response.json)) // Use a normal function to set the received state
} else {
dispatch(someError)
}
})
}
}
function setAccount(data) {
return { type: 'SET_Account', data: data }
}
10. What is the recommended way of accessing Redux store? 〒▽〒
The best way is to use the connect()
function using Higher Order Functions (HOCs) pattern. This allows to map state and action creators to the component, and have them passed in automatically as the store updates.
Example:
// You can pass the context as an option to connect
export default connect(
mapState,
mapDispatch,
null,
{ context: MyContext }
)(MyComponent)
// or, call connect as normal to start
const ConnectedComponent = connect(
mapState,
mapDispatch
)(MyComponent)
// Later, pass the custom context as a prop to the connected component
<ConnectedComponent context={MyContext} />
11. Why we use constants in Redux? ≧ ﹏ ≦
This is because they allow us to easily find all usages of a specific functionality across the project. Also, you won't get much ReferenceError
when you use constants.
Example:
// In constants.js
export const ADD_TODO = 'ADD_TODO'
export const DELETE_TODO = 'DELETE_TODO'
export const EDIT_TODO = 'EDIT_TODO'
// In actions.js
import { ADD_TODO } from './actionTypes';
export function addTodo(text) {
return { type: ADD_TODO, text }
}
// In reducer.js
import { ADD_TODO } from './actionTypes'
export default (state = [], action) => {
switch (action.type) {
case ADD_TODO:
return [
...state,
{
text: action.text,
completed: false
}
];
default:
return state
}
}
12. Why we use of ownProps
(Doc 📃) in mapStateToProps()
and mapDispatchToProps()
? (;′⌒')
The ownProps
is an optional parameter we add as the second argument in both mapStateToProps()
or mapDispatchToProps()
. You use this only if your component needs the data from its own props to retrieve data from the store.
Example:
// Todo.js
function mapStateToProps(state, ownProps) {
const { visibilityFilter } = state
const { id } = ownProps
const todo = getTodoById(state, id)
// component receives additionally:
return { todo, visibilityFilter }
}
// Later, in your application, a parent component renders:
<ConnectedTodo id={123} />
// and your component receives props.id, props.todo, and props.visibilityFilter
As the doc says:
You do not need to include values from
ownProps
in the object returned frommapStateToProps
.connect
will automatically merge those different prop sources into a final set of props.
13. What are the differences between call()
and put()
in redux-saga? (┬┬﹏┬┬)
redux-saga
is a library that aims to make application side effects easier to manage, more efficient to execute, easy to test, and better at handling failures.
> call()
(Doc 📃)
In simple words, you use call()
to create effect description, which instructs middleware to call the promise. After which the middleware invokes the function and examines its result.
> put()
(Doc 📃)
Whereas the put()
function creates an effect, which instructs middleware to dispatch an action to the store.
function* fetchUserSaga(action) {
// `call` function accepts rest arguments, which will be passed to `api.fetchUser` function.
// Instructing middleware to call promise, it resolved value will be assigned to `userData` variable
const userData = yield call(api.fetchUser, action.userId)
// Instructing middleware to dispatch corresponding action.
yield put({
type: 'FETCH_USER_SUCCESS',
userData
})
}
Hence, both call()
and put()
are effect creator functions.
14. What are the differences between redux-saga and redux-thunk? ╯︿╰
The 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.
- Thunk uses Promises to deal with them, whereas Saga uses Generators.
- Thunk is simple to use and Promises are familiar, Sagas/Generators are more powerful but you will need to learn them.
Here is a good post on how you can implement both of them:
Async React Basics with Redux-thunk & Redux-saga
Norbert Braun ・ Oct 8 '18
15. How to set initial state in Redux? ::>_<::
There are two ways:
- First, use the
createStore
method which accepts an optionalpreloadedState
value as its second argument.
Example:
const rootReducer = combineReducers({
todos: todos,
visibilityFilter: visibilityFilter
})
const initialState = {
todos: [{ id: 123, name: 'example', completed: false }]
}
const store = createStore(
rootReducer,
initialState
)
- Or by using explicit check inside the reducer.
Example:
function myReducer(state = someDefaultValue, action)
I hope I've explained these 15 points in the right way. Did you know about them? Honestly, I never knew about @ symbol in Redux!
Oh no... Better hit up @docsmsft for help: https://t.co/Gvjh0h4YTT
— Microsoft Developer UK (@msdevUK) April 15, 2020
Image source: https://t.co/d9j3psc1g1#DevHumour #Developer #Programming pic.twitter.com/8dvAr1f0hs
Top comments (0)