Redux Basics
Redux is a state management library that allows you to have a global state in the context of react. It means that you can have a state that is accessible from any component no matter where they are in the tree.
Important concepts
- 1. Store:
- global state that is going to be accessible across any component no matter where they are in the react application. store you get to define it in any way that you want
- Store is usually made up of multiple slices of the state. each responsible for a certain domain in the application
// store
interface CounterState {
value: number;
}
interface UserState {
isSignedIn: boolean;
}
- 2. Actions: what it should do
const increment = {type: "INCREMENT"} # payload is optional
const incrementByAmount = { type: "INCREMENT", payload: 10 }
- 3. Reducer
- responsible for taking an action and then depending on the type of the action will actually go out and make the update in the redux store. They will use the type of action to know what updates to do and optionally, they ewill use the payload to do those specific actions to make those specific updates to the Redux store.
- important to remember, that reducers will never directly make an update to the Redux store we have, the concept of immutability which means we are never allowed to directly mutate the state instead, what reducers are going to do is they're going to take the state and copy of the state and then make those changes to that copy of the state which will also have all the other unchanged properties of the state and then will completely replace the state as a whole with the copy that has the changes applied
- never going to directly mutate the state
- Redux does not work if you mutate the state directly
Let’s compare Redux and Redux Toolkit for state management in a tabular format:
Redux | Redux Toolkit | |
Purpose | State management library | Simplified version of Redux |
Core Concepts | Store, Actions, Reducers, middleware | Store, Actions, Reducers, Slices |
Boilerplate | Requires more boilerplate code | Reduces boilerplate with built-in utilities |
Reducers | Manually write reducers | createSlice generates reducers |
Actions | Define action types and creators | Automatically generates action creators |
Async Actions | Requires middleware (e.g., Redux Thunk) | Built-in support for handling asynchronous actions |
DevTools Integration | Requires manual setup | Integrated DevTools extension |
Opinionated Defaults | No built-in defaults | Provides opinionated defaults |
Learning Curve | Steeper learning curve | Easier for beginners |
Community Adoption | Widely adopted in large-scale apps | Gaining popularity |
In summary, Redux Toolkit streamlines Redux development by providing a simpler API, built-in support for async actions, and opinionated defaults. It’s a great choice for both beginners and experienced developers. However, if you prefer more control and are comfortable with the traditional Redux approach, you can stick with plain Redux.
Let’s explore some concrete examples of using Redux and Redux Toolkit for state management in a React application.
Redux Basics
Redux is a powerful state management library that helps you manage your application’s state in a predictable and efficient manner. Here’s a simple example of how to set up Redux:
- Install Dependencies: First, install the necessary packages:
-
npm install redux react-redux
- Create a Redux Store: Set up your Redux store in your store.js file:
// store.js import { createStore } from 'redux'; const initialState = { counter: 0, }; const reducer = (state = initialState, action) => { switch (action.type) { case 'INCREMENT': return { ...state, counter: state.counter + 1 }; case 'DECREMENT': return { ...state, counter: state.counter - 1 }; default: return state; } }; const store = createStore(reducer); export default store;
- Connect Redux to Your App: In your index.js or root component, wrap your app with the Provider from react-redux:
// index.js import React from 'react'; import ReactDOM from 'react-dom'; import { Provider } from 'react-redux'; import store from './store'; import App from './App'; ReactDOM.render( <Provider store={store}> <App /> </Provider>, document.getElementById('root') );
- Use Redux State in Components: Access the Redux state in your components using the useSelector hook:
// MyComponent.js import React from 'react'; import { useSelector, useDispatch } from 'react-redux'; const MyComponent = () => { const counter = useSelector((state) => state.counter); const dispatch = useDispatch(); return ( <div> <p>Counter: {counter}</p> <button onClick={() => dispatch({ type: 'INCREMENT' })}>Increment</button> <button onClick={() => dispatch({ type: 'DECREMENT' })}>Decrement</button> </div> ); }; export default MyComponent;
Redux Toolkit
Redux Toolkit simplifies working with Redux by providing utility functions and best practices. Let’s see how it improves our code:
- Install Redux Toolkit: Install the package:
-
npm install @reduxjs/toolkit
- Create a Slice: Define your state and reducers using a “slice”:
// counterSlice.js import { createSlice } from '@reduxjs/toolkit'; const counterSlice = createSlice({ name: 'counter', initialState: 0, reducers: { increment: (state) => state + 1, decrement: (state) => state - 1, }, }); export const { increment, decrement } = counterSlice.actions; export default counterSlice.reducer;
- Configure the Store with Toolkit: Use the configureStore function from Redux Toolkit:
// store.js import { configureStore } from '@reduxjs/toolkit'; import counterReducer from './counterSlice'; const store = configureStore({ reducer: { counter: counterReducer, }, }); export default store;
- Use Toolkit in Components: Same component as before, but now with Toolkit:
// MyComponent.js import React from 'react'; import { useSelector, useDispatch } from 'react-redux'; import { increment, decrement } from './counterSlice'; const MyComponent = () => { const counter = useSelector((state) => state.counter); const dispatch = useDispatch(); return ( <div> <p>Counter: {counter}</p> <button onClick={() => dispatch(increment())}>Increment</button> <button onClick={() => dispatch(decrement())}>Decrement</button> </div> ); }; export default MyComponent;
Redux Toolkit streamlines the process, making it easier to manage state and actions. Remember that these are simplified examples, and in real-world applications, you’ll likely have more complex state structures and additional slices for different parts of your app
Redux | Redux Toolkit | |
Installation | Requires installing redux and react-redux packages. | Requires installing @reduxjs/toolkit. |
Store Setup | Manually create a store with reducers. | Use configureStore function to set up the store. |
Reducers | Write reducers explicitly. | Define reducers using a “slice”. |
Actions | Define action types and creators manually. | Automatically generates action creators. |
Boilerplate Code | More boilerplate code for actions and reducers. | Reduces boilerplate significantly. |
Immutability | Handle immutability manually. | Immutability handled internally. |
DevTools Integration | Requires additional setup for Redux DevTools. | DevTools integration out of the box. |
Community Standard | Traditional approach, widely used. | Recommended by Redux maintainers. |
// index.js // 2. Connect Redux to Your App: In your index.js or root component, wrap your app with the Provider from react-redux: import React from 'react'; import ReactDOM from 'react-dom'; import { Provider } from 'react-redux'; import store from './store'; import App from './App'; ReactDOM.render( <Provider store={store}> <App /> </Provider>, document.getElementById('root') ); |
// counterSlice.ts // 1. Create a Slice: Define your state and reducers using a “slice” import { createSlice } from '@reduxjs/toolkit'; const initialState: CounterState = { value: 0} const counterSlice = createSlice({ name: 'counter', initialState, reducers: { increment: (state) => state.value += 1, decrement: (state) => state.value -= 1, incrementByAmount: (state, action: PayloadAction<number>) => { state.value += action.payload; } }, // handling asynct reducer extraReducers: (builder) => { builder.addCase(incrementAsync.fulfilled, (state, action) => { state.value += action.payload; }) } }); // async like fetching data from api export const incrementAsync = createAsyncThunk( "counter/incrementAsync", async (amount: number) => { await new Promise((resolve) => setTimeout(resolve, 1000)); return amount; } ) export const { increment, decrement, incrementByAmount } = counterSlice.actions; export default counterSlice.reducer; |
|
// store.js // 1. Create a Redux Store: Set up your Redux store in your store.js file: import { createStore } from 'redux'; const initialState = { counter: 0, }; const reducer = (state = initialState, action) => { switch (action.type) { case 'INCREMENT': return { ...state, counter: state.counter + 1 }; case 'DECREMENT': return { ...state, counter: state.counter - 1 }; default: return state; } }; const store = createStore(reducer); export default store; |
// store.ts // 2. Configure the Store with Toolkit: Use the configureStore function from Redux Toolkit: import { configureStore } from '@reduxjs/toolkit'; import counterReducer from './counterSlice'; export const store = configureStore({ reducer: { counter: counterReducer, }, }); # typescript thing export type RootState = ReturnType<typeof store.getState>; export type AppDispatch = typeof store.dispatch; |
|
// MyComponent.js // 3. Use Redux State in Components: Access the Redux state in your components using the useSelector hook import React from 'react'; import { useSelector, useDispatch } from 'react-redux'; const MyComponent = () => { const counter = useSelector((state) => state.counter); const dispatch = useDispatch(); return ( <div> <p>Counter: {counter}</p> <button onClick={() => dispatch({ type: 'INCREMENT' })}>Increment</button> <button onClick={() => dispatch({ type: 'DECREMENT' })}>Decrement</button> </div> ); }; export default MyComponent; |
// MyComponent.ts // 3. Use Toolkit in Components: Same component as before, but now with Toolkit import React from 'react'; import { useSelector, useDispatch } from 'react-redux'; import { increment, decrement } from './counterSlice'; const MyComponent = () => { const counter = useSelector((state) => state.counter); const dispatch = useDispatch(); return ( <div> <p>Counter: {counter}</p> <button onClick={() => dispatch(increment())}>Increment</button> <button onClick={() => dispatch(decrement())}>Decrement</button> </div> ); }; export default MyComponent; |
|
Counter.tsx | import { useSelector, useDispatch } form "react-redux"; import { RootState } from "../state/store"; import { decrement, increment, incrementByAmount } from "../state/counter/counterSlice"; const Counter = () => { const count = useSelector((state: RootState) => state.counter.value); const dispatch = useDispatch(); return <div> <h2>{count}</h2> <div> <button onClick={()=> dispatch(iincrementByAmount(10))}> Increment</button> <button onClick={()=> dispatch(decrement())}> Decrement</button> </div> </div>; } export default Counter; |
|
https://www.youtube.com/watch?v=5yEG6GhoJBs
'Dev & LLM' 카테고리의 다른 글
High-value AI use cases in business (0) | 2024.04.05 |
---|---|
Travel assistant capabilities use case (1) | 2024.03.27 |
4 Methods of Prompt Engineering (0) | 2024.03.13 |
LLM - How Large Language Models Work & What are Generative AI models? (0) | 2024.03.13 |
Typescript - interface, type, class 이해하기 (0) | 2024.03.09 |
댓글