DEV Community

Cover image for React Component Design Patterns - Part 2
Fatemeh Paghar
Fatemeh Paghar

Posted on

React Component Design Patterns - Part 2

5. State reducer pattern

The State Reducer Pattern in React is a design pattern used to manage state in a more controlled and predictable manner. It involves using a reducer function to handle state transitions and actions, similar to how reducers work in Redux. This pattern can be particularly useful for managing complex state logic or state that needs to be shared across multiple components.

Let's walk through an example of implementing the State Reducer Pattern in React for managing a a social media dashboard application.In this example, we'll manage the state of the user's posts and notifications using a reducer function.

import React, { useReducer } from 'react';

// Action types
const ADD_POST = 'ADD_POST';
const DELETE_POST = 'DELETE_POST';
const ADD_NOTIFICATION = 'ADD_NOTIFICATION';
const DELETE_NOTIFICATION = 'DELETE_NOTIFICATION';

// Reducer function
const dashboardReducer = (state, action) => {
  switch (action.type) {
    case ADD_POST:
      return { ...state, posts: [...state.posts, action.payload] };
    case DELETE_POST:
      return { ...state, posts: state.posts.filter(post => post.id !== action.payload.id) };
    case ADD_NOTIFICATION:
      return { ...state, notifications: [...state.notifications, action.payload] };
    case DELETE_NOTIFICATION:
      return { ...state, notifications: state.notifications.filter(notification => notification.id !== action.payload.id) };
    default:
      return state;
  }
};

// Dashboard component
const Dashboard = () => {
  // Initialize state using useReducer hook
  const [state, dispatch] = useReducer(dashboardReducer, { posts: [], notifications: [] });

  // Add post function
  const addPost = (text) => {
    const newPost = { id: Date.now(), text };
    dispatch({ type: ADD_POST, payload: newPost });
  };

  // Delete post function
  const deletePost = (id) => {
    dispatch({ type: DELETE_POST, payload: { id } });
  };

  // Add notification function
  const addNotification = (text) => {
    const newNotification = { id: Date.now(), text };
    dispatch({ type: ADD_NOTIFICATION, payload: newNotification });
  };

  // Delete notification function
  const deleteNotification = (id) => {
    dispatch({ type: DELETE_NOTIFICATION, payload: { id } });
  };

  return (
    <div>
      <h1>Social Media Dashboard</h1>
      <div>
        <h2>Posts</h2>
        <ul>
          {state.posts.map(post => (
            <li key={post.id}>
              {post.text}
              <button onClick={() => deletePost(post.id)}>Delete</button>
            </li>
          ))}
        </ul>
        <button onClick={() => addPost('New post')}>Add Post</button>
      </div>
      <div>
        <h2>Notifications</h2>
        <ul>
          {state.notifications.map(notification => (
            <li key={notification.id}>
              {notification.text}
              <button onClick={() => deleteNotification(notification.id)}>Dismiss</button>
            </li>
          ))}
        </ul>
        <button onClick={() => addNotification('New notification')}>Add Notification</button>
      </div>
    </div>
  );
};

export default Dashboard;

Enter fullscreen mode Exit fullscreen mode

In this example:

  • We define action types (ADD_POST, DELETE_POST, ADD_NOTIFICATION, DELETE_NOTIFICATION) that represent different actions that can be performed on the social media dashboard state.
  • We define a reducer function (dashboardReducer) that takes the current state and an action and returns the new state based on the action.
  • We use the useReducer hook to create a stateful value (state) and a dispatch function to update the state by passing actions to the reducer.
  • The Dashboard component contains logic for adding and deleting posts and notifications. It dispatches actions to the reducer to update the state accordingly.
  • In the UI, posts, and notifications are rendered as list items with buttons to delete them.
  • Users can add new posts or notifications by clicking on the respective buttons.

This example demonstrates how to implement the State Reducer Pattern in React to manage the state of a social media dashboard, including posts and notifications. The reducer function centralizes state management logic, making it easier to understand and maintain.

6- Component Composition pattern

Component Composition pattern involves composing smaller, reusable components together to create more complex components or UIs. Let's illustrate this pattern with a social media dashboard example.

Suppose we have a social media dashboard that consists of several components such as UserInfo, Feed, Notifications, and Sidebar. We can compose these smaller components together to create the social media dashboard component.

import React from 'react';

// UserInfo component
const UserInfo = ({ user }) => {
  return (
    <div className="user-info">
      <img src={user.profilePic} alt="Profile" />
      <h3>{user.name}</h3>
    </div>
  );
};

// Feed component
const Feed = ({ posts }) => {
  return (
    <div className="feed">
      <h2>Feed</h2>
      <ul>
        {posts.map((post, index) => (
          <li key={index}>
            <h4>{post.title}</h4>
            <p>{post.content}</p>
          </li>
        ))}
      </ul>
    </div>
  );
};

// Notifications component
const Notifications = ({ notifications }) => {
  return (
    <div className="notifications">
      <h2>Notifications</h2>
      <ul>
        {notifications.map((notification, index) => (
          <li key={index}>
            <p>{notification}</p>
          </li>
        ))}
      </ul>
    </div>
  );
};

// Sidebar component
const Sidebar = () => {
  return (
    <div className="sidebar">
      <ul>
        <li>Home</li>
        <li>Profile</li>
        <li>Messages</li>
        <li>Settings</li>
      </ul>
    </div>
  );
};

// SocialMediaDashboard component composed of smaller components
const SocialMediaDashboard = ({ user, posts, notifications }) => {
  return (
    <div className="social-media-dashboard">
      <UserInfo user={user} />
      <div className="main-content">
        <Feed posts={posts} />
        <Notifications notifications={notifications} />
      </div>
      <Sidebar />
    </div>
  );
};

// Main component
const App = () => {
  // Sample data
  const user = {
    name: "John Doe",
    profilePic: "https://via.placeholder.com/150",
  };

  const posts = [
    { title: "Post 1", content: "Content of post 1" },
    { title: "Post 2", content: "Content of post 2" },
    { title: "Post 3", content: "Content of post 3" },
  ];

  const notifications = [
    "You have a new friend request",
    "Your post has been liked",
    "You have a new message",
  ];

  return (
    <div>
      <h1>Social Media Dashboard</h1>
      <SocialMediaDashboard user={user} posts={posts} notifications={notifications} />
    </div>
  );
};

export default App;

Enter fullscreen mode Exit fullscreen mode

In this example:

  • We have separate components for UserInfo, Feed, Notifications, and Sidebar, each responsible for rendering a specific part of the social media dashboard.
  • We compose these smaller components together in the SocialMediaDashboard component to create the entire social media dashboard UI.
  • The App component serves as the entry point where we pass sample data (user information, posts, notifications) to the SocialMediaDashboard component.

This demonstrates how we can use the Component Composition pattern to build a social media dashboard by composing smaller, reusable components together. Each component focuses on a specific aspect of the UI, promoting reusability and maintainability.

References:

Top comments (4)

Collapse
 
importantlybig profile image
Troda

Can I ask a question that:

  • Passing props to "SocialMediaDashboard " component that makes it re-render. So, If I do not want the component re-render when the props "notifications" is passed so the pattern number 6 still oke ? Thank you
Collapse
 
fpaghar profile image
Fatemeh Paghar

The notifications prop is a constant variable and it does not cause rendering in the component. If It is a state or context, yes It is caused by rerendering in the component.

On the other hand, the Component Composition pattern concept breaks down a big component into many reusable small components. It helps to have clean code and code readability. Rerendering is another react concept and maybe we need to render SocialMediaDashboard with changes in props and state. It is normal in the nested component in React.

Collapse
 
importantlybig profile image
Troda

thank you so much

Collapse
 
morganmartinez profile image
Morgan Martinez • Edited

Thank you so much for this. Since you helped me, I also want to help you with your college or school writing work, like essays and assignments. If you are wondering why I am sharing this ukwritings.com/assignment-help website with you, then I want to share a few things that made me want to share it with you. They have a professional team of writers on many topics. and the second best is that they are so affordable, which makes it easy for all students to hire them.