DEV Community

Gianni Castellano
Gianni Castellano

Posted on

Redux the beginners guide!

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:

  1. Store
  2. Actions
  3. Reducers
  4. Dispatch
  5. 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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Top comments (0)