React Lifecycle Project: System Activity Monitor

Last Updated : 5 Oct, 2026

Build a Course Enrollment Dashboard using React class components. The project will demonstrate how components are created, updated, removed, and how errors in child components can be handled.

The project will use React lifecycle methods to monitor changes in course enrollment and manage the component lifecycle.

Step-by-Step Implementation

1. Create the Basic Course Dashboard

First, create a class component that displays the course name, instructor, and number of enrolled students.

JavaScript
import React, { Component } from "react";

class CourseCard extends Component {
    constructor(props) {
        super(props);

        this.state = {
            students: 25
        };
    }

    addStudent = () => {
        this.setState((prevState) => ({
            students: prevState.students + 1
        }));
    };

    render() {
        return (
            <div style={styles.card}>
                <h2>{this.props.name}</h2>

                <p>Instructor: {this.props.instructor}</p>

                <p>
                    Students Enrolled:{" "}
                    <strong>{this.state.students}</strong>
                </p>

                <button
                    style={styles.button}
                    onClick={this.addStudent}
                >
                    Add Student
                </button>
            </div>
        );
    }
}

class App extends Component {
    render() {
        return (
            <div style={styles.page}>
                <h1>Course Enrollment Dashboard</h1>

                <p style={styles.intro}>
                    Manage course enrollment using React class
                    components.
                </p>

                <CourseCard
                    name="React Development"
                    instructor="Alex Johnson"
                />
            </div>
        );
    }
}

const styles = {
    page: {
        minHeight: "100vh",
        padding: "60px 20px",
        background: "#f4f7fb",
        fontFamily: "Arial, sans-serif",
        color: "#222"
    },

    intro: {
        color: "#666"
    },

    card: {
        maxWidth: "500px",
        margin: "30px auto",
        padding: "25px",
        background: "#fff",
        border: "1px solid #ddd",
        borderRadius: "12px"
    },

    button: {
        padding: "10px 18px",
        border: "none",
        borderRadius: "6px",
        background: "#087f5b",
        color: "#fff",
        cursor: "pointer"
    }
};

export default App;

Output

Screenshot-2026-09-12-160339

Here, CourseCard stores the number of enrolled students in its state. Clicking Add Student updates that state and displays the new count.

2. Add Mounting and Update Lifecycle Methods

Now use componentDidMount() to detect when the course component is added to the screen.

Use componentDidUpdate() to detect changes to the enrollment count.

JavaScript
import React, { Component } from "react";

class CourseCard extends Component {
    constructor(props) {
        super(props);

        this.state = {
            students: 25,
            lifecycleStatus: "Component created"
        };
    }

    componentDidMount() {
        this.setState({
            lifecycleStatus: "Component mounted"
        });

        console.log("CourseCard mounted");
    }

    componentDidUpdate(prevProps, prevState) {
        if (prevState.students !== this.state.students) {
            console.log(
                `Enrollment changed from ${prevState.students} to ${this.state.students}`
            );
        }
    }

    addStudent = () => {
        this.setState((prevState) => ({
            students: prevState.students + 1,
            lifecycleStatus: "Component updated"
        }));
    };

    render() {
        return (
            <div style={styles.card}>
                <span style={styles.label}>ACTIVE COURSE</span>

                <h2>{this.props.name}</h2>

                <p>Instructor: {this.props.instructor}</p>

                <div style={styles.enrollment}>
                    <span>Students Enrolled</span>
                    <strong>{this.state.students}</strong>
                </div>

                <button
                    style={styles.button}
                    onClick={this.addStudent}
                >
                    Add Student
                </button>

                <p style={styles.status}>
                    Lifecycle: {this.state.lifecycleStatus}
                </p>
            </div>
        );
    }
}

class App extends Component {
    render() {
        return (
            <div style={styles.page}>
                <h1>Course Enrollment Dashboard</h1>

                <p style={styles.intro}>
                    Monitor course enrollment using React
                    lifecycle methods.
                </p>

                <CourseCard
                    name="React Development"
                    instructor="Alex Johnson"
                />
            </div>
        );
    }
}

const styles = {
    page: {
        minHeight: "100vh",
        padding: "60px 20px",
        background: "#f4f7fb",
        fontFamily: "Arial, sans-serif",
        color: "#222"
    },

    intro: {
        color: "#666",
        textAlign: "center"
    },

    card: {
        maxWidth: "500px",
        margin: "30px auto",
        padding: "25px",
        background: "#fff",
        border: "1px solid #ddd",
        borderRadius: "12px",
        boxShadow: "0 5px 18px rgba(0, 0, 0, 0.06)"
    },

    label: {
        fontSize: "12px",
        fontWeight: "bold",
        color: "#087f5b"
    },

    enrollment: {
        display: "flex",
        justifyContent: "space-between",
        padding: "15px 0",
        borderTop: "1px solid #eee",
        borderBottom: "1px solid #eee"
    },

    button: {
        marginTop: "20px",
        padding: "10px 18px",
        border: "none",
        borderRadius: "6px",
        background: "#087f5b",
        color: "#fff",
        cursor: "pointer"
    },

    status: {
        fontSize: "13px",
        color: "#666"
    }
};

export default App;

Output

Screenshot-2026-09-12-160112

componentDidMount() runs after the component is mounted. When the student count changes, componentDidUpdate() runs after the component updates.

3. Prevent Unnecessary Updates and Handle Unmounting

Next, add shouldComponentUpdate() to decide whether the CourseCard should update.

The dashboard will also include a button to remove or show the course. When the course is removed, componentWillUnmount() runs.

JavaScript
import React, { Component } from "react";

class CourseCard extends Component {
    constructor(props) {
        super(props);

        this.state = {
            students: 25,
            lifecycleStatus: "Component created"
        };
    }

    componentDidMount() {
        this.setState({
            lifecycleStatus: "Component mounted"
        });

        console.log("CourseCard mounted");
    }

    shouldComponentUpdate(nextProps, nextState) {
        return (
            nextState.students !== this.state.students ||
            nextState.lifecycleStatus !== this.state.lifecycleStatus
        );
    }

    componentDidUpdate(prevProps, prevState) {
        if (prevState.students !== this.state.students) {
            console.log(
                `Enrollment changed from ${prevState.students} to ${this.state.students}`
            );
        }
    }

    componentWillUnmount() {
        console.log("CourseCard is being removed");
    }

    addStudent = () => {
        this.setState((prevState) => ({
            students: prevState.students + 1,
            lifecycleStatus: "Component updated"
        }));
    };

    render() {
        return (
            <div style={styles.card}>
                <span style={styles.label}>ACTIVE COURSE</span>

                <h2>{this.props.name}</h2>

                <p>Instructor: {this.props.instructor}</p>

                <div style={styles.enrollment}>
                    <span>Students Enrolled</span>
                    <strong>{this.state.students}</strong>
                </div>

                <button
                    style={styles.button}
                    onClick={this.addStudent}
                >
                    Add Student
                </button>

                <p style={styles.status}>
                    Lifecycle: {this.state.lifecycleStatus}
                </p>
            </div>
        );
    }
}

class App extends Component {
    state = {
        showCourse: true
    };

    toggleCourse = () => {
        this.setState((prevState) => ({
            showCourse: !prevState.showCourse
        }));
    };

    render() {
        return (
            <div style={styles.page}>
                <div style={styles.container}>
                    <h1 style={styles.heading}>
                        Course Enrollment Dashboard
                    </h1>

                    <p style={styles.intro}>
                        Monitor course enrollment using React
                        lifecycle methods.
                    </p>

                    <button
                        style={styles.toggleButton}
                        onClick={this.toggleCourse}
                    >
                        {this.state.showCourse
                            ? "Remove Course"
                            : "Show Course"}
                    </button>

                    {this.state.showCourse && (
                        <CourseCard
                            name="React Development"
                            instructor="Alex Johnson"
                        />
                    )}
                </div>
            </div>
        );
    }
}

const styles = {
    page: {
        minHeight: "100vh",
        padding: "60px 20px",
        background: "#f4f7fb",
        fontFamily: "Arial, sans-serif",
        color: "#222"
    },

    container: {
        maxWidth: "650px",
        margin: "0 auto"
    },

    heading: {
        margin: "0 0 8px",
        textAlign: "center"
    },

    intro: {
        margin: "0 0 25px",
        color: "#666",
        textAlign: "center",
        lineHeight: "1.6"
    },

    toggleButton: {
        display: "block",
        margin: "0 auto 25px",
        padding: "10px 18px",
        border: "1px solid #333",
        borderRadius: "7px",
        background: "#fff",
        color: "#222",
        cursor: "pointer"
    },

    card: {
        padding: "25px",
        background: "#fff",
        border: "1px solid #ddd",
        borderRadius: "14px",
        boxShadow: "0 5px 18px rgba(0, 0, 0, 0.06)"
    },

    label: {
        fontSize: "12px",
        fontWeight: "bold",
        color: "#087f5b"
    },

    enrollment: {
        display: "flex",
        justifyContent: "space-between",
        padding: "15px 0",
        borderTop: "1px solid #eee",
        borderBottom: "1px solid #eee"
    },

    button: {
        marginTop: "20px",
        padding: "10px 18px",
        border: "none",
        borderRadius: "7px",
        background: "#087f5b",
        color: "#fff",
        cursor: "pointer"
    },

    status: {
        marginBottom: 0,
        fontSize: "13px",
        color: "#666"
    }
};

export default App;

Output

Screenshot-2026-09-12-155820

Now clicking Remove Course removes CourseCard from the UI, which causes componentWillUnmount() to run.

The shouldComponentUpdate() method checks the next state before allowing the component to update.

4. Add Error Handling with an Error Boundary

Finally, add an Error Boundary to handle errors that occur while rendering the course component.

JavaScript
import React, { Component } from "react";

class CourseCard extends Component {
    constructor(props) {
        super(props);

        this.state = {
            students: 25,
            lifecycleStatus: "Component created"
        };
    }

    componentDidMount() {
        this.setState({
            lifecycleStatus: "Component mounted"
        });

        console.log("CourseCard mounted");
    }

    shouldComponentUpdate(nextProps, nextState) {
        return (
            nextState.students !== this.state.students ||
            nextState.lifecycleStatus !== this.state.lifecycleStatus
        );
    }

    componentDidUpdate(prevProps, prevState) {
        if (prevState.students !== this.state.students) {
            console.log(
                `Enrollment changed from ${prevState.students} to ${this.state.students}`
            );
        }
    }

    componentWillUnmount() {
        console.log("CourseCard is being removed");
    }

    addStudent = () => {
        this.setState((prevState) => ({
            students: prevState.students + 1,
            lifecycleStatus: "Component updated"
        }));
    };

    render() {
        if (this.props.hasError) {
            throw new Error("Course data could not be loaded.");
        }

        return (
            <div style={styles.card}>
                <div style={styles.icon}>âš›</div>

                <div style={styles.content}>
                    <span style={styles.label}>ACTIVE COURSE</span>

                    <h2 style={styles.title}>
                        {this.props.name}
                    </h2>

                    <p style={styles.instructor}>
                        Instructor: {this.props.instructor}
                    </p>

                    <div style={styles.enrollment}>
                        <span>Students Enrolled</span>

                        <strong style={styles.count}>
                            {this.state.students}
                        </strong>
                    </div>

                    <button
                        style={styles.addButton}
                        onClick={this.addStudent}
                    >
                        Add Student
                    </button>

                    <p style={styles.status}>
                        Lifecycle: {this.state.lifecycleStatus}
                    </p>
                </div>
            </div>
        );
    }
}

class ErrorBoundary extends Component {
    constructor(props) {
        super(props);

        this.state = {
            hasError: false
        };
    }

    static getDerivedStateFromError() {
        return {
            hasError: true
        };
    }

    componentDidCatch(error, info) {
        console.error("Course Error:", error);
        console.error("Error Information:", info);
    }

    render() {
        if (this.state.hasError) {
            return (
                <div style={styles.errorBox}>
                    <h2>Course could not be displayed</h2>

                    <p>
                        Something went wrong while loading the
                        course.
                    </p>
                </div>
            );
        }

        return this.props.children;
    }
}

class App extends Component {
    state = {
        showCourse: true,
        simulateError: false
    };

    toggleCourse = () => {
        this.setState((prevState) => ({
            showCourse: !prevState.showCourse
        }));
    };

    simulateCourseError = () => {
        this.setState({
            simulateError: true
        });
    };

    render() {
        return (
            <div style={styles.page}>
                <div style={styles.container}>
                    <h1 style={styles.heading}>
                        Course Enrollment Dashboard
                    </h1>

                    <p style={styles.intro}>
                        Monitor course enrollment using React
                        class lifecycle methods.
                    </p>

                    <div style={styles.actions}>
                        <button
                            style={styles.toggleButton}
                            onClick={this.toggleCourse}
                        >
                            {this.state.showCourse
                                ? "Remove Course"
                                : "Show Course"}
                        </button>

                        {this.state.showCourse && (
                            <button
                                style={styles.errorButton}
                                onClick={this.simulateCourseError}
                            >
                                Simulate Error
                            </button>
                        )}
                    </div>

                    {this.state.showCourse && (
                        <ErrorBoundary
                            key={this.state.simulateError ? "error" : "normal"}
                        >
                            <CourseCard
                                name="React Development"
                                instructor="Alex Johnson"
                                hasError={this.state.simulateError}
                            />
                        </ErrorBoundary>
                    )}
                </div>
            </div>
        );
    }
}

const styles = {
    page: {
        minHeight: "100vh",
        padding: "60px 20px",
        boxSizing: "border-box",
        background: "#f4f7fb",
        fontFamily: "Arial, sans-serif",
        color: "#222"
    },

    container: {
        maxWidth: "700px",
        margin: "0 auto"
    },

    heading: {
        margin: "0 0 8px",
        textAlign: "center"
    },

    intro: {
        margin: "0 0 25px",
        textAlign: "center",
        color: "#666",
        lineHeight: "1.6"
    },

    actions: {
        display: "flex",
        justifyContent: "center",
        gap: "10px",
        marginBottom: "25px"
    },

    toggleButton: {
        padding: "10px 18px",
        border: "1px solid #333",
        borderRadius: "7px",
        background: "#fff",
        color: "#222",
        cursor: "pointer"
    },

    errorButton: {
        padding: "10px 18px",
        border: "1px solid #c92a2a",
        borderRadius: "7px",
        background: "#fff",
        color: "#c92a2a",
        cursor: "pointer"
    },

    card: {
        display: "flex",
        gap: "20px",
        padding: "25px",
        background: "#fff",
        border: "1px solid #ddd",
        borderRadius: "14px",
        boxShadow: "0 5px 18px rgba(0, 0, 0, 0.06)"
    },

    icon: {
        display: "flex",
        alignItems: "center",
        justifyContent: "center",
        width: "70px",
        height: "70px",
        flexShrink: 0,
        borderRadius: "12px",
        background: "#e7f5ff",
        fontSize: "34px"
    },

    content: {
        flex: 1
    },

    label: {
        fontSize: "12px",
        fontWeight: "bold",
        color: "#087f5b"
    },

    title: {
        margin: "7px 0"
    },

    instructor: {
        margin: "0 0 20px",
        color: "#666"
    },

    enrollment: {
        display: "flex",
        justifyContent: "space-between",
        alignItems: "center",
        paddingTop: "15px",
        borderTop: "1px solid #eee"
    },

    count: {
        fontSize: "20px"
    },

    addButton: {
        marginTop: "20px",
        padding: "10px 18px",
        border: "none",
        borderRadius: "7px",
        background: "#087f5b",
        color: "#fff",
        cursor: "pointer",
        fontSize: "14px"
    },

    status: {
        marginTop: "15px",
        marginBottom: 0,
        fontSize: "13px",
        color: "#666"
    },

    errorBox: {
        padding: "25px",
        background: "#fff4f4",
        border: "1px solid #ffc9c9",
        borderRadius: "12px",
        textAlign: "center"
    }
};

export default App;

Output

Screenshot-2026-09-12-155540

The Simulate Error button makes the child component throw an error during rendering. The ErrorBoundary catches the error and displays a fallback message.

componentDidCatch() also logs the error information to the console.

Final Source Code

The final project is contained in one App.jsx file.

JavaScript
import React, { Component } from "react";

class CourseCard extends Component {
    constructor(props) {
        super(props);

        this.state = {
            students: 25,
            lifecycleStatus: "Component created"
        };
    }

    componentDidMount() {
        this.setState({
            lifecycleStatus: "Component mounted"
        });

        console.log("CourseCard mounted");
    }

    shouldComponentUpdate(nextProps, nextState) {
        return (
            nextState.students !== this.state.students ||
            nextState.lifecycleStatus !== this.state.lifecycleStatus
        );
    }

    componentDidUpdate(prevProps, prevState) {
        if (prevState.students !== this.state.students) {
            console.log(
                `Enrollment changed from ${prevState.students} to ${this.state.students}`
            );
        }
    }

    componentWillUnmount() {
        console.log("CourseCard is being removed");
    }

    addStudent = () => {
        this.setState((prevState) => ({
            students: prevState.students + 1,
            lifecycleStatus: "Component updated"
        }));
    };

    render() {
        if (this.props.hasError) {
            throw new Error("Course data could not be loaded.");
        }

        return (
            <div style={styles.card}>
                <div style={styles.icon}>âš›</div>

                <div style={styles.content}>
                    <span style={styles.label}>ACTIVE COURSE</span>

                    <h2 style={styles.title}>
                        {this.props.name}
                    </h2>

                    <p style={styles.instructor}>
                        Instructor: {this.props.instructor}
                    </p>

                    <div style={styles.enrollment}>
                        <span>Students Enrolled</span>

                        <strong style={styles.count}>
                            {this.state.students}
                        </strong>
                    </div>

                    <button
                        style={styles.addButton}
                        onClick={this.addStudent}
                    >
                        Add Student
                    </button>

                    <p style={styles.status}>
                        Lifecycle: {this.state.lifecycleStatus}
                    </p>
                </div>
            </div>
        );
    }
}

class ErrorBoundary extends Component {
    constructor(props) {
        super(props);

        this.state = {
            hasError: false
        };
    }

    static getDerivedStateFromError() {
        return {
            hasError: true
        };
    }

    componentDidCatch(error, info) {
        console.error("Course Error:", error);
        console.error("Error Information:", info);
    }

    render() {
        if (this.state.hasError) {
            return (
                <div style={styles.errorBox}>
                    <h2>Course could not be displayed</h2>

                    <p>
                        Something went wrong while loading the
                        course.
                    </p>
                </div>
            );
        }

        return this.props.children;
    }
}

class App extends Component {
    state = {
        showCourse: true,
        simulateError: false
    };

    toggleCourse = () => {
        this.setState((prevState) => ({
            showCourse: !prevState.showCourse
        }));
    };

    simulateCourseError = () => {
        this.setState({
            simulateError: true
        });
    };

    render() {
        return (
            <div style={styles.page}>
                <div style={styles.container}>
                    <h1 style={styles.heading}>
                        Course Enrollment Dashboard
                    </h1>

                    <p style={styles.intro}>
                        Monitor course enrollment using React
                        class lifecycle methods.
                    </p>

                    <div style={styles.actions}>
                        <button
                            style={styles.toggleButton}
                            onClick={this.toggleCourse}
                        >
                            {this.state.showCourse
                                ? "Remove Course"
                                : "Show Course"}
                        </button>

                        {this.state.showCourse && (
                            <button
                                style={styles.errorButton}
                                onClick={this.simulateCourseError}
                            >
                                Simulate Error
                            </button>
                        )}
                    </div>

                    {this.state.showCourse && (
                        <ErrorBoundary
                            key={
                                this.state.simulateError
                                    ? "error"
                                    : "normal"
                            }
                        >
                            <CourseCard
                                name="React Development"
                                instructor="Alex Johnson"
                                hasError={this.state.simulateError}
                            />
                        </ErrorBoundary>
                    )}
                </div>
            </div>
        );
    }
}

const styles = {
    page: {
        minHeight: "100vh",
        padding: "60px 20px",
        boxSizing: "border-box",
        background: "#f4f7fb",
        fontFamily: "Arial, sans-serif",
        color: "#222"
    },

    container: {
        maxWidth: "700px",
        margin: "0 auto"
    },

    heading: {
        margin: "0 0 8px",
        textAlign: "center"
    },

    intro: {
        margin: "0 0 25px",
        textAlign: "center",
        color: "#666",
        lineHeight: "1.6"
    },

    actions: {
        display: "flex",
        justifyContent: "center",
        gap: "10px",
        marginBottom: "25px"
    },

    toggleButton: {
        padding: "10px 18px",
        border: "1px solid #333",
        borderRadius: "7px",
        background: "#fff",
        color: "#222",
        cursor: "pointer"
    },

    errorButton: {
        padding: "10px 18px",
        border: "1px solid #c92a2a",
        borderRadius: "7px",
        background: "#fff",
        color: "#c92a2a",
        cursor: "pointer"
    },

    card: {
        display: "flex",
        gap: "20px",
        padding: "25px",
        background: "#fff",
        border: "1px solid #ddd",
        borderRadius: "14px",
        boxShadow: "0 5px 18px rgba(0, 0, 0, 0.06)"
    },

    icon: {
        display: "flex",
        alignItems: "center",
        justifyContent: "center",
        width: "70px",
        height: "70px",
        flexShrink: 0,
        borderRadius: "12px",
        background: "#e7f5ff",
        fontSize: "34px"
    },

    content: {
        flex: 1
    },

    label: {
        fontSize: "12px",
        fontWeight: "bold",
        color: "#087f5b"
    },

    title: {
        margin: "7px 0"
    },

    instructor: {
        margin: "0 0 20px",
        color: "#666"
    },

    enrollment: {
        display: "flex",
        justifyContent: "space-between",
        alignItems: "center",
        paddingTop: "15px",
        borderTop: "1px solid #eee"
    },

    count: {
        fontSize: "20px"
    },

    addButton: {
        marginTop: "20px",
        padding: "10px 18px",
        border: "none",
        borderRadius: "7px",
        background: "#087f5b",
        color: "#fff",
        cursor: "pointer",
        fontSize: "14px"
    },

    status: {
        marginTop: "15px",
        marginBottom: 0,
        fontSize: "13px",
        color: "#666"
    },

    errorBox: {
        padding: "25px",
        background: "#fff4f4",
        border: "1px solid #ffc9c9",
        borderRadius: "12px",
        textAlign: "center"
    }
};

export default App;

Output

Comment