Hi everyone! I am here to give a small guide explaining on how to use Redux within to manage state within your react application. There are five key concepts to understand in order to incorporate redux in your react application:
- Store
- Actions
- Reducers
- Dispatch
- Selectors
Store
The purpose of store is to have a central hub where all the state within the app is managed. After creating a Store.jsx file, you need to first import configureStore at the top of the file. Then you need to import the files within your app that are modifying states from the slice files. Lastly, you need to combine the reducers from all the slice files into a single root reducer.
Actions
Within your slice files it is necessary to use actions in order to send data from within your app to the Store file. They are generic Javascript objects that require a type property in order to perform the needed action. In the context of my latest project I used them in order to fetch, delete, create, and post data to my backend server.
Reducers
Reducers work hand in hand with actions. They are functions that take the current state and an action as arguments and return a new state without mutating the original data. They state how the app's state will change due to an action.
Dispatch
Just like reducers, dispatch, is a function that works with reducers to send actions to the store file. The dispatch function is used to trigger the reducer which in turn triggers action in order to update the state.
Selectors
Selectors are used in order to grab the data from the state within files in order to update the data accordingly.
Here is code snippets from my latest project in order to give a visual demonstration on the above information
Store.jsx:
import { configureStore } from "@reduxjs/toolkit"
import clanReducer from './ClanSlice'
import eventReducer from './EventSlice'
import clanFormReducer from './ClanFormSlice'
import eventFormReducer from './EventFormSlice'
const store = configureStore({
reducer: {
clan: clanReducer,
event: eventReducer,
clanForm: clanFormReducer,
eventForm: eventFormReducer,
},
})
export default store
ClanSlice.jsx
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'
import axios from 'axios'
export const fetchClan = createAsyncThunk('clans/fetchClan', async (id) => {
const response = await axios.get(`http://127.0.0.1:5555/clans/${id}`)
return response.data
})
export const deleteClan = createAsyncThunk('clans/deleteClan', async (id) => {
await axios.delete(`http://127.0.0.1:5555/clans/${id}`)
return id
})
export const fetchClans = createAsyncThunk('clans/fetchClans', async () => {
const response = await axios.get('http://127.0.0.1:5555/clans')
return response.data
})
export const createClan = createAsyncThunk('clans/createClan', async (clanData, { dispatch }) => {
const response = await axios.post('http://127.0.0.1:5555/clans', clanData)
dispatch(fetchClans())
return response.data
})
const clanSlice = createSlice({
name: 'clan',
initialState: {
clans: [],
clan: [],
status: 'idle',
error: null,
},
reducers: {
resetClan: (state) => {
state.clan = []
state.status = 'idle'
state.error = null
},
resetStatus: (state) => {
state.status = 'loading'
}
},
extraReducers: (builder) => {
builder
.addCase(fetchClan.pending, (state) => {
state.status = 'loading'
})
.addCase(fetchClan.fulfilled, (state, action) => {
state.status = 'succeeded'
state.clan = action.payload
})
.addCase(fetchClan.rejected, (state, action) => {
state.status = 'failed'
state.error = action.error.message
})
.addCase(fetchClans.pending, (state) => {
state.status = 'loading'
})
.addCase(fetchClans.fulfilled, (state, action) => {
state.status = 'succeeded'
state.clans = action.payload
})
.addCase(fetchClans.rejected, (state, action) => {
state.status = 'failed'
state.error = action.error.message
})
.addCase(deleteClan.fulfilled, (state, action) => {
state.clans = state.clans.filter(clan => clan.id !== action.payload)
})
.addCase(createClan.fulfilled, (state, action) => {
state.status = 'succeeded'
state.clans.push(action.payload)
})
},
})
export const { resetClan, resetStatus } = clanSlice.actions
export default clanSlice.reducer
ClanSlice.jsx
import React, { useEffect } from "react"
import { useDispatch, useSelector } from "react-redux"
import { fetchClans, resetStatus } from "./ClanSlice"
import { Link } from "react-router-dom"
import './Clans.css'
function Clans() {
const dispatch = useDispatch()
const clans = useSelector((state) => state.clan.clans)
const status = useSelector((state) => state.clan.status)
const error = useSelector((state) => state.clan.error)
useEffect(() => {
dispatch(fetchClans())
}, [dispatch])
useEffect(() => {
return () => {
dispatch(resetStatus())
}
}, [])
let content
if (status === 'loading') {
content = <p>Loading...</p>
} else if (status === 'succeeded') {
content = (
<div className="clans-wrapper">
{clans.map((clan) => (
<div key={clan.id} className="clan-box">
<h3>
<Link to={`/clans/${clan.id}`}>{clan.name}</Link>
</h3>
<p>{clan.description}</p>
<h4>Members:</h4>
<div className="members-container">
<ul>
{clan.members.map((member) => (
<li key={member.id}>
<span className="username-text">Username:</span>{member.username} <span className="role-text">Role:</span> {member.role}
<ul>
{member.participations.map((participation) => (
<li key={participation.id}>
<span className="event-text">Event:</span> {participation.event.event}
<br />
<span className="status-text">Status:</span> {participation.participation_status}
</li>
))}
</ul>
</li>
))}
</ul>
</div>
<h4>Events:</h4>
<div className="events-container">
<ul>
{clan.events.map((event) => (
<li key={event.id}>
<Link to={`/events/${event.id}`}>{event.event}</Link> - {event.date} - {event.location} - {event.details}
</li>
))}
</ul>
</div>
</div>
))}
</div>
)
} else if (status === 'failed') {
content = <p>{error}</p>
}
return (
<div className="container">
<h2>Welcome to the Clans Page</h2>
{content}
</div>
)
}
export default Clans
Top comments (0)