Tony Drummond
Tony Drummond

Reputation: 375

React/Redux Reset State Upon Route Change

EDIT: In the code I show below the user NEVER sees the message because the state is reset. The screenshot is merely an example to show the message that I'd like to get displayed.

Current State: Upon successful add to the database, "Facility created successfully" message NEVER shows. However, if I remove the reset code, when the user navigates away from the page and comes back, the message persists. The root cause of this is that my state in Redux has a "success" setting upon successful add to the database. If I do a reset upon the success message, the user never sees it because the state is reset to an empty object.enter image description here

Ideal State: User adds facility to database, success message comes back via Redux state and message shows. The page refreshes showing the newly added facility. The user navigates away from the page and then the Redux state is reset.

I've tried libraries from React to detect unmount in addition to using a cleanup function in useEffect to no avail.

I will list my code below and appreciate any feedback or insights. Note that some irrelevant code has been trimmed for the sake of space.

FacilityAdminScreen.js (component)

import React, { useState, useEffect } from "react";
import { Form, Row, Button, Col } from "react-bootstrap";
import { useDispatch, useSelector } from "react-redux";
import Message from "../components/Message";
import Loader from "../components/Loader";
import { chunk } from "lodash";
import FacilityCard from "../components/FacilityCard";
import { createFacility, getFacilityAllBasicInfo } from "../actions/facilityActions";
import { FACILITY_ADD_TO_DATABASE_RESET } from "../constants/facilityConstants";

const FacilityAdminScreen = ({ match }) => {
    // State for form information
    const [name, setName] = useState("");
    const [streetAddress1, setStreetAddress1] = useState("");
    const [streetAddress2, setStreetAddress2] = useState("");
    const [city, setCity] = useState("");
    const [state, setState] = useState("");
    const [zip, setZip] = useState("");

    const dispatch = useDispatch();

    // List of facilities for populating page
    const facilityAllBasicInfo = useSelector(state => state.facilityAllBasicInfo);
    const { loading, error } = facilityAllBasicInfo;
    const facilities = chunk(facilityAllBasicInfo.facilities, 2);

    // Response upon adding facility to database
    const facilityAddToDatabase = useSelector(state => state.facilityAddToDatabase);
    const {
        loading: loadingCreate,
        error: errorCreate,
        success: successCreate,
    } = facilityAddToDatabase;

    const submitHandler = e => {
        e.preventDefault();
        // Attempt to create the facility
        dispatch(
            createFacility({
                company: match.params.companyId,
                name,
                streetAddress1,
                streetAddress2,
                city,
                state,
                zip,
            })
        );
    };

    useEffect(() => {
        // Get all facilities for company
        dispatch(getFacilityAllBasicInfo(match.params.companyId));
        // If facility created successfully, reset all form state
        if (successCreate) {
            dispatch({ type: FACILITY_ADD_TO_DATABASE_RESET });
            setName("");
            setStreetAddress1("");
            setStreetAddress2("");
            setCity("");
            setState("");
            setZip("");
        }
    }, [dispatch, successCreate, match.params.companyId]);

    return (
        <>
            <Row>
                <Col md={8}>
                    <h1>Facilities</h1>
                    <h6>Click facility name for detailed information</h6>
                    {loading ? (
                        <Loader />
                    ) : error ? (
                        <Message variant="danger">{error}</Message>
                    ) : (
                        <>
                            {facilities.map((facilityArray, i) => (
                                <Row className="mb-3" key={i}>
                                    {facilityArray.map((facility, i) => (
                                        <Col md={6} key={i}>
                                            <FacilityCard
                                                key={facility._id}
                                                companyId={match.params.companyId}
                                                id={facility._id}
                                                name={facility.name}
                                                streetAddress1={facility.streetAddress1}
                                                streetAddress2={facility.streetAddress2}
                                                city={facility.city}
                                                state={facility.state}
                                                zip={facility.zip}
                                                isActive={facility.isActive}
                                            />
                                        </Col>
                                    ))}
                                </Row>
                            ))}
                        </>
                    )}
                </Col>
                <Col md={4}>
                    <h1>Add Facility</h1>
                    {loadingCreate && <Loader />}
                    {errorCreate && <Message variant="danger">{errorCreate}</Message>}
                    {successCreate && (
                        <Message variant="success">Facility created successfully</Message>
                    )}
                    <Form onSubmit={submitHandler}>
                        {/*Trimmed code here for the sake of space */}
                    </Form>
                </Col>
            </Row>
        </>
    );
};

export default FacilityAdminScreen;

Reducers for BOTH getting the list of facilities and adding to database:

export const facilityAllBasicInfoReducer = (state = { facilities: [] }, action) => {
    switch (action.type) {
        case FACILITY_ALL_BASIC_INFO_REQUEST:
            return { loading: true };
        case FACILITY_ALL_BASIC_INFO_SUCCESS:
            return { loading: false, facilities: action.payload };
        case FACILITY_ALL_BASIC_INFO_FAIL:
            return { loading: false, error: action.payload };
        default:
            return state;
    }
};

export const facilityAddToDatabaseReducer = (state = {}, action) => {
    switch (action.type) {
        case FACILITY_ADD_TO_DATABASE_REQUEST:
            return { loading: true };
        case FACILITY_ADD_TO_DATABASE_SUCCESS:
            return { loading: false, success: true, facility: action.payload };
        case FACILITY_ADD_TO_DATABASE_FAIL:
            return { loading: false, error: action.payload };
        case FACILITY_ADD_TO_DATABASE_RESET:
            return {};
        default:
            return state;
    }
};

Upvotes: 0

Views: 922

Answers (1)

Developer-Person
Developer-Person

Reputation: 71

I don't think that your implementation of a cleanup function within useEffect that you copied in above is the right way to do it. The issue you are having is that the dispatch to reset the state is fired off straight after success becomes true.

I think what you are looking for is a way to use componentWillUnmount using Hooks, so that when you navigate away from the page and the component is destroyed it fires off an action that modifies the state the way you want.

The return statement on useEffect does this. Whatever you include in the return statement will be fired off when the component unmounts.

    useEffect(() => {
        dispatch(//action to add facility);
        return () => {
            dispatch(//action to modify state when component unmounts);
        };
    }, [dispatch]); 

Using the above, when you navigate back to the page success will again be false.

Hopefully this helps.

Upvotes: 1

Related Questions