React-Redux tips
If you are reading this article, you might have been familiar with Redux and have used it with React. But what if I give you some useful hints to feel more comfortable with this stack.
Avoid having one reducer
It’s not bad if an application is small. But when it goes to build a more complex business app, better to opt in forcombineReducers(reducer)
solution.
Modify store.js
file.
import { createStore, combineReducers } from 'redux'import { user } from './modules/user/user.reducer.js'
import { product } from './modules/user/product.reducer.js'
import { account } from './modules/user/account.reducer.js'export const store = createStore(combineReducers({ user, product, account }))
Pros?
- make reducers readable;
- make your application structured;
- facilitate the testing;
Proxy access to the state
Once you place reducers in the functional module, you can access the state using selector
. It’s a function that takes a state
as a parameter and returns information (only the props needed for a component).
export const getProduct = productId => ({ product: { list } }) =>
list.find(product => product.id === productId)
// MyComponent.jsconst MyComponent = () => {
const product = useSelector(getProduct(12))
return <div>{product.name}</div>
}
Prefix the name of your actions
Actions are written in uppercase letters separated by ‘_’ (ex. SET_USERS).
app/
modules/
user/
components/
user.actions.js <--- a place where all action creators are exported
user.reducer.js
user.selectors.js
Always test your reducers
As you know, reducers
hold business of your application and manipulate the state
. A reducer is a single function that takes 2 parameters so the code is easy to test.
Here is an example of a simple test using Jest:
describe('ReducerName', () => {
beforeEach(() => {
// Init a new state
})
describe('ACTION', () => {
// Group tests by action type
it('should test action with some params', () => {})
it('should test action with other params', () => {})
})
describe('SECOND_ACTION', () => {
it('should test action with some params', () => {})
})
})
Use custom middlewares
The most common example is handling HTTP calls during an action that uses redux-thunk
.
export const foo = () =>
fetch('https://example.com/api/foo')
.then(data => ({ type: 'FOO', data }))
.catch(error => {
// Do something
})
export const bar = () =>
fetch('https://example.com/api/bar')
.then(data => ({ type: 'BAR', data }))
.catch(error => {
// Do something
})
These two functions are actually the same thing so we can refactor it. First, we need to define a middleware that would take care of this behavior.
const http = store => next => async action => {
if (action.http) {
try {
action.result = await fetch(action.http)
} catch (error) {
// Do something
}
}
return next(action)
}
// in redux store init
const exampleApp = combineReducers(reducers)
const store = createStore(exampleApp, applyMiddleware(http))
These actions can be implemented in a much simpler way avoiding code duplication.
export const foo = () => ({ type: 'FOO', http: 'https://example.com/api/foo' })
export const bar = () => ({ type: 'BAR', http: 'https://example.com/api/bar' })
Conclusion
For extra info go there -> https://redux.js.org/introduction/getting-started