Redux is a predictable state container for creating JavaScript applications. It helps you in writing applications that behave consistently, run in different environments (client, server, and native), and are easy to test. You can say that Redux is a state management tool.
The way Redux works is simple. A central store holds the entire state of the application and each component can access the stored state without having to send the down props from one component to another.
There are three building parts in Redux: actions, store, and reducers.
- Actions in Redux: Actions are events and the only way you can send data from your application to the Redux store. The data can either be from user interactions, API calls, or even form submissions.
Actions are sent using the method store.dispatch(). Actions are the plain JavaScript objects, and they must contain a type property to indicate the type of action to be carried out. They must have a payload that contains the information that should be worked on by the action. Actions are created via an action creator.
- Reducers in Redux: Reducers are the pure functions that take current state of an application, perform an action, and return a new state. These states are always stored as objects, and specify how the state of an application changes in response to the action sent to the store.
It is also based on the reduce function in JavaScript, where a single value is calculated from the multiple values after a callback function has been carried out.
- Store in Redux: The store holds the application state. There is only one store available in any Redux application. You can access the state stored, update the state, and register or unregister listeners via helper methods.
Other benefits of using Redux are:
- Redux make the state predictable: In Redux, the state always remain predictable. If the same state and actions are passed to a reducer, the same result is always produced since reducers are pure functions. The state is also immutable and never change. This makes it possible to implement the arduous tasks such as infinite undo and redo. It is possible to implement time travel, i.e, the ability to move back and forth between the previous states and view the results in real-time.
- Maintainability: Redux is strict about how the code should be organized that makes it easier for someone having knowledge of Redux to understand the structure of any application of Redux. This makes it easier to maintain.
- Debuggable for days: Redux also makes it easy to debug an application. By logging the actions and state, it is easy to understand coding errors, network errors, and other forms of bugs that may come up during production.
- Ease of testing: It is also easy to test Redux apps since functions are used to change pure functions.
- State persistence: You can also persist some of the app’s state to local storage and restore it after a refresh.
- Server-side rendering: Redux can be used for server-side rendering. With it, you can handle the app’s initial render by sending the state of an app to the server along with the response to the server request. The required components are rendered in HTML and sent to the clients.
How to Create a React App using Redux?
Step 1: src/redux/users.js
import { createSlice } from 'redux-starter-kit'; import faker from 'faker'; const users = [...new Array(1000)].map(() => ({ Â Â id: faker.random.uuid(), Â Â avatar: faker.image.avatar(), Â Â username: faker.internet.userName(), Â Â name: `${faker.name.firstName()} ${faker.name.lastName()}`, })); const { actions, reducer } = createSlice({ Â Â initialState: { Â Â Â Â users, Â Â Â Â selected: null, Â Â }, Â Â reducers: { Â Â Â Â selectUser(state, { payload: user }) { Â Â Â Â Â Â state.selected = user || null; Â Â Â Â }, Â Â }, }); export const { selectUser } = actions; export default reducer;
Step 2: You’ll also need to create a store to manage the state for each piece.
src/redux/index.js import { configureStore } from 'redux-starter-kit'; import users from './users'; export default configureStore({ Â Â reducer: { Â Â Â Â users, Â Â }, });
Step 3: Now in your main App.js file, you will need to set up the React Redux provider so that the components anywhere in the tree can be connected to the store.
src/App.js // Add below lines of code to the top of the file import { Provider } from 'react-redux'; import store from './redux'; // At the bottom of your file, replace the export default App with the following: export default () => ( Â Â <Provider store={store}> Â Â Â Â <App /> Â Â </Provider> );
Step 4: Now we will add the UI.
src/Header.js import React from 'react'; import { connect } from 'react-redux'; import logo from './logo.svg'; import { Container, Menu, Image } from 'semantic-ui-react'; const Header = ({ pageName }) => (   <Menu fixed="top" inverted>     <Container>       <Menu.Item header>         <Image size="mini" src={logo} />         User Search       </Menu.Item>       <Menu.Item style={{ flex: 1 }}>{pageName}</Menu.Item>     </Container>   </Menu> ); const mapStateToProps = state => ({   pageName: state.users.selected ? state.users.selected.name : '', }); export default connect(mapStateToProps)(Header);
Step 5: Next create the search component:
src/Search.js import React from 'react'; import { connect } from 'react-redux'; import { Container, Search } from 'semantic-ui-react'; import Fuse from 'fuse.js'; import { selectUser } from './users'; const SearchPage = ({ users, selectUser }) => {   const [term, setTerm] = React.useState('');   const filteredUsers = React.useMemo(() => {     if (!term) return users;     const fuse = new Fuse(users, {       shouldSort: true,       keys: ['name', 'username'],     });     return fuse.search(term);   }, [users, term]);   return (     <Container>       <Search         onResultSelect={(e, { result }) => {           setTerm('');           selectUser(result.user);         }}         onSearchChange={e => setTerm(e.currentTarget.value)}         results={filteredUsers.slice(0, 5).map(user => ({           childKey: user.id,           title: user.name,           description: user.username,           image: user.avatar,           user,         }))}         value={term}       />     </Container>   ); }; const mapStateToProps = state => ({   users: state.users.users, }); const mapDispatchToProps = { selectUser }; export default connect(   mapStateToProps,   mapDispatchToProps, )(SearchPage);
Step 6: Next, create a component to display the selected user:
src/SelectedUser.js import React from 'react'; import { connect } from 'react-redux'; import { Message, Card, Image } from 'semantic-ui-react'; import { selectUser } from './users'; const SelectedUser = ({ selected, selectUser }) => {   if (!selected) return null;   return (     <Card style={{ marginTop: '2em' }}>       <Message         attached         header={selected.name}         onDismiss={() => selectUser(null)}       />       <Image src={selected.avatar} wrapped ui={false} />       <Card.Content>         <Card.Header>{selected.username}</Card.Header>       </Card.Content>     </Card>   ); }; const mapStateToProps = state => ({   selected: state.users.selected, }); const mapDispatchToProps = { selectUser }; export default connect(   mapStateToProps,   mapDispatchToProps, )(SelectedUser);
Step 7: In your src/index.js file, replace the ./index.css import content with the Semantic UI css.
import ‘semantic-ui-css/semantic.min.css’;
Your App.js file will now need to reference the new components you just made. Your App.js file should look like this:
src/App.js import React from 'react'; import { Provider } from 'react-redux'; import { Container } from 'semantic-ui-react'; import store from './redux'; import Header from './Header'; import Search from './Search'; import SelectedUser from './SelectedUser'; const App = () => ( Â Â <div> Â Â Â Â <Header /> Â Â Â Â <Container style={{ paddingTop: '7em' }}> Â Â Â Â Â Â <Search /> Â Â Â Â Â Â <SelectedUser /> Â Â Â Â </Container> Â Â </div> ); export default () => ( Â Â <Provider store={store}> Â Â Â Â <App /> Â Â </Provider> );