Using Redux with React
Redux is a separate library; therefore, various "binding" libraries help users use Redux with some UI framework. The React-Redux UI bindings library officially is a package separate from the Redux core, which needs to be installed too:
npm install react-redux
You can also try this code with Online Javascript Compiler
Run Code
Designing The Component Tree
Similar to designing the state structure according to the requirements, users can design the overall set of UI components and define how they relate to each other in that application.
Based on requirements for the todo app concept used in previous blogs, we will need at least this set of components:
-
<App>:for rendering everything else in the application, the root component.
- <Header>: containing the text input for "new todo" and the checkbox for "complete all todos".
-
<TodoList>: after applying filters, the list of all visible todo items.
- <TodoListItem>: an individual todo item with a toggle checkbox to check or uncheck the todo item’s completed status, along with a colour category selector.
- <Footer>: To show the number of active todo items and filtering controls based on completed status and colour category.
Reading State Store with useSelector
We will first look at one of the React-Redux hooks- the useSelector hook, which is called in the React function components, will give access to React state values.
We will create the <TodoList> component to read the todo item list from the store, loop over them, and show an individual <TodoListItem> component for each todo entry.
useSelector accepts a single function, called a selector. Selectors take the entire store state as an argument and then read the state value and return it as a result.
E.g.: The todo app state stores the array of todos as state.todos. A small selector function to return this array for todos is given below:
const selectTodo = state => state.todos
You can also try this code with Online Javascript Compiler
Run Code
For the todos marked as completed:
const selectCompletedTodos = state =>
{
const completedTodos = state.todos.filter(todo =>
todo.completed)
return completedTodos.length
}
You can also try this code with Online Javascript Compiler
Run Code
Dispatching Actions with useDispatch
To dispatch actions from a component to the store outside of React, the solution is to call store.dispatch(action). Since we cannot access the store in a component file, there needs to be a way to access the dispatch function itself inside the components. In React, the React-Redux useDispatch hook provides the store's dispatch method as its result. Calling const dispatch = useDispatch() in components needing to dispatch actions, and then calling dispatch(someAction) will be the solution.
In the <Header> component, suppose the users want to type in some new todo item and dispatch the action {type: 'todos/todoAdded'}, containing that text.. The action will be dispatched when the users press the enter key.
src/features/header/Header.js
import React, { useState } from 'react'
import { useDispatch } from 'react-redux'
const Header = () =>
{
const [text, setText] = useState('')
const dispatch = useDispatch()
const handleChange = e => setText(e.target.value)
const handleKeyDown = e =>
{
const trimmedText = e.target.value.trim()
// if user presses the enter key
if (e.key === 'Enter' && trimmedText)
{
// dispatching the "todo added"
dispatch({ type: 'todos/todoAdded', payload: trimmedText })
// clearing the input
setText('')
}
}
return
(
<input
type="text"
placeholder="What needs to be done?"
autoFocus={true}
value={text}
onChange={handleChange}
onKeyDown={handleKeyDown}
/>
)
}
export default Header
You can also try this code with Online Javascript Compiler
Run Code
Passing the Store with Provider
A hook being a JS function, can't import a store from store.js all by itself. Users need to tell what store they want to use in their components to React-Redux. This can be done by rendering a <Provider> component around the whole <App> and finally passing the store as a prop to <Provider>.
Adding it to the main index.js file:src/index.js
import React from 'react'
import ReactDOM from 'react-dom'
import {Provider} from 'react-redux'
import App from './App'
import store from './store'
ReactDOM.render
(
// Render a `<Provider>` around the entire `<App>`,
// and pass the Redux store to as a prop
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>,
document.getElementById('root')
)
You can also try this code with Online Javascript Compiler
Run Code
React-Redux Patterns
Global State, Component State and Forms
The app state doesn’t always have to go to the redux store. Only the global state needed across the app must go in the Redux store. The state required in only one place could be kept in the component state.
Multiple Selectors in a Component
At present, it’s only the <TodoList> component reading the data from the store. If the <Footer> component needs to read data too, it will need the following information:
- The number of todos.
- The filter value for the current “status”.
- List of currently selected “colour” categories.
To read these values, the useSelector could be called within one component multiple times.
Selecting Data in List Items by ID
The app that we have built so far has some performance issues. Since the <TodoList> is currently reading the entire state and passing individual todos as props, any todo object is updated, new memory references of the object and the state array are made. This in turn, triggers the UseSelector hook, which recursively rerenders components. This becomes a big performance issue in bigger apps, and hence, needs an optimal solution.
The solution for this is to pass just an array of IDs, such that each <TodoListItem> receives a unique ID. Then, we use a hook from React-Redux called ShallowEqual. The UseSelector hook can take a comparison function as a second argument, ShallowEqual in this case. ShallowEqual checks if the array’s contents have changed or not, and UseSelector only re-renders whenever the comparison function returns true.
src/features/todos/TodoList.js
import React from 'react'
import { useSelector, shallowEqual } from 'react-redux'
import TodoListItem from './TodoListItem'
const selectTodoIds = state => state.todos.map(todo => todo.id)
const TodoList = () =>
{
const todoIds = useSelector(selectTodoIds, shallowEqual)
const renderedListItems = todoIds.map(todoId => {
return <TodoListItem key={todoId} id={todoId} />
})
return <ul className="todo-list">{renderedListItems}</ul>
}
You can also try this code with Online Javascript Compiler
Run Code
Frequently Asked Questions
Q1. What is the useSelector?
Ans. The useSelector hook is a function that takes the present state of the app as an argument and returns the data required from it.
Q2. How can we select multiple properties in Redux?
Ans. Multiple useSelector hooks with each hook selecting a single property in the store is one of the options to select multiple properties. Another option would be having one useSelector() hook and one selector that combines several properties from the store into a single object.
Q3. What is the difference between Redux and hooks?
Ans. Redux holds the global state and actions to be dispatched, while React Hooks only handle the local component state.
Q4. What is the difference between connect and useSelector?
Ans. Both useSelector() and connect() are React-redux hooks that could have the same outcome. However, connect() could be used both with class and function components in React, while hooks can only be used with function components. While it is simpler to use hooks, they also allow fewer performance tweaks as compared to connect.
Key takeaways
In this article, we learned about UI’s integration with Redux and understood how React and Redux work together while focusing mainly on the useSelector.
You can go to Coding Ninjas Studio to try and solve more problems for practice. Share this blog with your friends if you found it helpful! Until then, All the best for your future endeavours, and Keep Coding.