React.js Series Finale: Redux, Next.js & Deployment

Welcome to the culmination of our React.js journey! Today we’ll explore professional state management, server-side rendering, and how to deploy your React applications like a senior developer.

State Management with Redux Toolkit

1. Modern Redux Setup

// store.js
import { configureStore } from '@reduxjs/toolkit';

const counterSlice = createSlice({
  name: 'counter',
  initialState: { value: 0 },
  reducers: {
    increment: state => { state.value += 1 },
    decrement: state => { state.value -= 1 },
  }
});

export const store = configureStore({
  reducer: {
    counter: counterSlice.reducer
  }
});

export const { increment, decrement } = counterSlice.actions;

2. Using Redux in Components

// Counter.js
import { useSelector, useDispatch } from 'react-redux';
import { increment, decrement } from './store';

function Counter() {
  const count = useSelector(state => state.counter.value);
  const dispatch = useDispatch();

  return (
    <div>
      <button onClick={() => dispatch(decrement())}>-</button>
      <span>{count}</span>
      <button onClick={() => dispatch(increment())}>+</button>
    </div>
  );
}

3. Async Thunks for API Calls

// usersSlice.js
export const fetchUsers = createAsyncThunk('users/fetch', async () => {
  const response = await fetch('/api/users');
  return await response.json();
});

const usersSlice = createSlice({
  name: 'users',
  initialState: { data: [], status: 'idle' },
  extraReducers: (builder) => {
    builder
      .addCase(fetchUsers.pending, (state) => {
        state.status = 'loading';
      })
      .addCase(fetchUsers.fulfilled, (state, action) => {
        state.data = action.payload;
        state.status = 'succeeded';
      });
  }
});

Server-Side Rendering with Next.js

1. Next.js Page Structure

// pages/index.js
export default function Home({ posts }) {
  return (
    <div>
      {posts.map(post => (
        <article key={post.id}>
          <h2>{post.title}</h2>
          <p>{post.excerpt}</p>
        </article>
      ))}
    </div>
  );
}

export async function getStaticProps() {
  const res = await fetch('https://api.example.com/posts');
  const posts = await res.json();

  return {
    props: { posts },
    revalidate: 60 // Regenerate every 60 seconds
  };
}

2. Dynamic Routes

// pages/posts/[id].js
export default function Post({ post }) {
  return (
    <article>
      <h1>{post.title}</h1>
      <div dangerouslySetInnerHTML={{ __html: post.content }} />
    </article>
  );
}

export async function getServerSideProps(context) {
  const { id } = context.params;
  const res = await fetch(`https://api.example.com/posts/${id}`);
  const post = await res.json();

  return { props: { post } };
}

3. API Routes

// pages/api/users.js
export default function handler(req, res) {
  if (req.method === 'GET') {
    res.status(200).json(users);
  } else if (req.method === 'POST') {
    const newUser = req.body;
    users.push(newUser);
    res.status(201).json(newUser);
  }
}

Deployment Strategies

1. Static Export (Next.js)

// next.config.js
module.exports = {
  output: 'export', // Enables static HTML export
  images: {
    unoptimized: true // For static hosting
  }
};

// Deploy to any static host:
// Vercel, Netlify, GitHub Pages, AWS S3

2. Node.js Server Deployment

// server.js for Express
const express = require('express');
const next = require('next');

const dev = process.env.NODE_ENV !== 'production';
const app = next({ dev });
const handle = app.getRequestHandler();

app.prepare().then(() => {
  const server = express();
  
  server.all('*', (req, res) => {
    return handle(req, res);
  });

  server.listen(3000, () => {
    console.log('Ready on http://localhost:3000');
  });
});

3. Docker Configuration

# Dockerfile
FROM node:18-alpine

WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .

RUN npm run build

EXPOSE 3000
CMD ["npm", "start"]

Complete Example: E-Commerce App

// Features implemented:
// - Next.js pages with getStaticProps
// - Redux Toolkit for cart management
// - API routes for checkout
// - Static export for CDN hosting

// pages/_app.js
import { Provider } from 'react-redux';
import { store } from '../redux/store';

export default function App({ Component, pageProps }) {
  return (
    <Provider store={store}>
      <Component {...pageProps} />
    </Provider>
  );
}

// pages/cart.js
import { useSelector } from 'react-redux';

export default function CartPage() {
  const items = useSelector(state => state.cart.items);
  
  return (
    <div>
      {items.map(item => (
        <CartItem key={item.id} item={item} />
      ))}
    </div>
  );
}

Series Recap

  1. Part 1: React Fundamentals
  2. Part 2: Components & Props
  3. Part 3: State & Events
  4. Part 4: Side Effects
  5. Part 5: Advanced Patterns
  6. Part 6: Performance & Testing
  7. Part 7: Production Ready Apps

Where to Go From Here?

  • Learn TypeScript for type-safe React
  • Explore React Native for mobile apps
  • Master GraphQL with Apollo Client
  • Dive into Webpack/Vite configurations

Thank you for following this series! We’ve covered everything from JSX basics to deploying production apps. What React topics would you like us to cover next? Let us know in the comments! 🎉

Full code examples available on GitHub repository.


Complete Series Navigation:

🔹 Part 1: Introduction to React
🔹 Part 2: JSX, Components & Props
🔹 Part 3: State and Events
🔹 Part 4: useEffect & Conditional Rendering
🔹 Part 5: Custom Hooks & Context API
🔹 Part 6: Advanced Patterns & Optimization
🔹 Part 7: Redux & Next.js (Finale)

Leave a Comment

Your email address will not be published. Required fields are marked *