Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

React Advanced Concepts: Context, Hooks, Redux, Routing, and Configuration

Tech 2

Cross-Component Data Passing with Context

React's Context API allows data to be passed through the component tree without manually passing props at every level.

import { useContext, createContext } from "react"

const MessageContext = createContext()

function ComponentA() {
  const message = useContext(MessageContext)
  return (
    <div>
      ComponentA - {message}
      <ComponentB />
    </div>
  )
}

function ComponentB() {
  const message = useContext(MessageContext)
  return <div>ComponentB - {message}</div>
}

function App() {
  const messageText = 'Hello from Context'
  return (
    <div>
      <MessageContext.Provider value={messageText}>
        <ComponentA />
      </MessageContext.Provider>
    </div>
  )
}

export default App

Managing Side Effects with useEffect

The useEffect hook handles side effects in functional components, with different behaviors based on dependency arrays.

function CounterApp() {
  const [count, setCount] = useState(0)
  
  // Executes on initial render and when count changes
  useEffect(() => {
    console.log("Side effect executed")
  }, [count])
  
  return (
    <div>
      <button onClick={() => setCount(count + 1)}>
        Count: {count}
      </button>
    </div>
  )
}

Cleaning Up Side Effects

Return a cleanup function from useEffect to prevent memory leaks when components umount.

function TimerComponent() {
  useEffect(() => {
    const intervalId = setInterval(() => {
      console.log("Timer running...")
    }, 1000)
    
    return () => {
      clearInterval(intervalId)
    }
  }, [])
  
  return <div>Timer Component</div>
}

function ParentComponent() {
  const [isVisible, setIsVisible] = useState(true)
  
  return (
    <div>
      {isVisible && <TimerComponent />}
      <button onClick={() => setIsVisible(false)}>
        Remove Timer Component
      </button>
    </div>
  )
}

Creating Custom Hooks

Custom hooks encapsulate reusable logic and must start with "use".

function useVisibilityToggle() {
  const [isVisible, setIsVisible] = useState(true)
  const toggleVisibility = () => setIsVisible(!isVisible)
  
  return {
    isVisible,
    toggleVisibility
  }
}

function ToggleApp() {
  const { isVisible, toggleVisibility } = useVisibilityToggle()
  
  return (
    <div>
      {isVisible && <div>Content is visible</div>}
      <button onClick={toggleVisibility}>
        Toggle Content
      </button>
    </div>
  )
}

State Management with Reddux Toolkit

Project Setup

npx create-react-app react-redux-app
npm install @reduxjs/toolkit react-redux

Store Structure

store/
  modules/
    counterStore.js
  index.js

Counter Store Implementation

import { createSlice } from "@reduxjs/toolkit"

const counterSlice = createSlice({
  name: 'counter',
  initialState: {
    value: 10
  },
  reducers: {
    increment(state) {
      state.value++
    },
    decrement(state) {
      state.value--
    },
    setValue(state, action) {
      state.value = action.payload
    }
  }
})

const { increment, decrement, setValue } = counterSlice.actions
const counterReducer = counterSlice.reducer

export { increment, decrement, setValue }
export default counterReducer

Store Configuration

import { configureStore } from "@reduxjs/toolkit"
import counterReducer from "./modules/counterStore"

const store = configureStore({
  reducer: {
    counter: counterReducer
  }
})

export default store

Application Setup

import store from './store'
import { Provider } from 'react-redux'

<React.StrictMode>
  <Provider store={store}>
    <App />
  </Provider>
</React.StrictMode>

Component Usage

import { useSelector, useDispatch } from "react-redux"
import { increment, decrement } from "./store/modules/counterStore"

function CounterApp() {
  const { value } = useSelector(state => state.counter)
  const dispatch = useDispatch()
  
  return (
    <div>
      Current Value: {value}
      <button onClick={() => dispatch(increment())}>+</button>
      <button onClick={() => dispatch(decrement())}>-</button>
    </div>
  )
}

Asynchronous Actions

import axios from "axios"

const counterSlice = createSlice({
  name: 'counter',
  initialState: {
    items: []
  },
  reducers: {
    updateItems(state, action) {
      state.items = action.payload
    }
  }
})

const { updateItems } = counterSlice.actions

const API_URL = 'https://api.example.com/data'

export const fetchData = () => {
  return async (dispatch) => {
    const response = await axios.get(API_URL)
    dispatch(updateItems(response.data))
  }
}

Routing with React Router

Installation

npm install react-router-dom

Router Configuration

import { createBrowserRouter } from 'react-router-dom'
import Login from '../pages/Login'
import Article from '../pages/Article'

const router = createBrowserRouter([
  {
    path: '/login',
    element: <Login />
  },
  {
    path: '/article',
    element: <Article />
  }
])

export default router

Application Setup

import { RouterProvider } from 'react-router-dom'
import router from './router'

root.render(
  <React.StrictMode>
    <RouterProvider router={router} />
  </React.StrictMode>
)

Navigation Methods

import { Link, useNavigate } from "react-router-dom"

function LoginPage() {
  const navigate = useNavigate()
  
  return (
    <div>
      Login Page
      {/* Declarative Navigation */}
      <Link to='/article'>Go to Article</Link>
      
      {/* Programmatic Navigation */}
      <button onClick={() => navigate('/article')}>
        Navigate Programmatically
      </button>
    </div>
  )
}

Route Parameters

Query Parameters

// Navigation
<button onClick={() => navigate('/article?id=123&name=example')}>
  Navigate with Query
</button>

// Receiving parameters
import { useSearchParams } from "react-router-dom"

function ArticlePage() {
  const [searchParams] = useSearchParams()
  const id = searchParams.get('id')
  const name = searchParams.get('name')
  
  return <div>Article ID: {id}, Name: {name}</div>
}

Path Parameters

// Router configuration
const router = createBrowserRouter([
  {
    path: '/article/:id',
    element: <Article />
  }
])

// Navigation
<button onClick={() => navigate('/article/123')}>
  Navigate with Path Parameter
</button>

// Receiving parameters
import { useParams } from "react-router-dom"

function ArticlePage() {
  const params = useParams()
  const id = params.id
  
  return <div>Article ID: {id}</div>
}

Nested Routes

import { Link, Outlet } from "react-router-dom"

function Layout() {
  return (
    <div>
      <div>Layout Component</div>
      <Link to='/'>Login</Link>
      <Link to='/article'>Article</Link>
      <Outlet />
    </div>
  )
}

Essential Packages

# Core packages
npm install @reduxjs/toolkit react-redux react-router-dom

# Utility packages
npm install dayjs classnames axios lodash

# Styling
npm install antd-mobile normalize.css
npm install --save-dev sass

# Rich text editor (React 18 compatible)
npm install react-quill@2.0.0-beta.2 --legacy-peer-deps

Path Alias Configuration

Installation

npm install --save-dev @craco/craco

Configuration Files

craco.config.js

const path = require('path')

module.exports = {
  webpack: {
    alias: {
      '@': path.resolve(__dirname, 'src')
    }
  }
}

jsconfig.json

{
  "compilerOptions": {
    "baseUrl": "./",
    "paths": {
      "@/*": ["src/*"]
    }
  }
}

package.json Updates

{
  "scripts": {
    "start": "craco start",
    "build": "craco build",
    "test": "craco test"
  }
}

Mock API Server with JSON Server

JSON Server provides a full fake REST API for development and testing.

Theme Configuration

Customize application themes through configuration files and component props.

<Button color="primary">Primary Button</Button>

Application Entry Point Cofniguration

import React from 'react'
import ReactDOM from 'react-dom/client'
import './index.css'
import { RouterProvider } from 'react-router-dom'
import { Provider } from 'react-redux'
import router from './router'
import store from './store'

const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(
  <Provider store={store}>
    <RouterProvider router={router} />
  </Provider>
)

Rich Text Editor Integration

import ReactQuill from 'react-quill'
import 'react-quill/dist/quill.snow.css'

function EditorComponent() {
  return (
    <ReactQuill
      className="editor-container"
      theme="snow"
      placeholder="Enter content here"
    />
  )
}

// CSS Styling
.editor-container {
  .ql-editor {
    min-height: 300px;
  }
}

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.