React Advanced Concepts: Context, Hooks, Redux, Routing, and Configuration
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;
}
}