DEV Community

Pamela Torres-Rocca
Pamela Torres-Rocca

Posted on

#State Management in a React Application using Redux for Beginners

State vs props in react can be a difficult concept for beginners to wrap their heads around. State is private in the component while props can be viewed by the user and not changed. Frameworks such as React and state management tools such as Redux keep an updated copy of the state in one location. State management becomes a more complex issue the larger the application becomes due to increased dependency between the components.

Redux can be used with multiple frameworks and I used it in my project with React. In my application the "state" told the application who the user was, a history of charges and/or payments made to their account, as well as which departments were associated with the payments and charges. A global management tool reduces the amount of prop passing that you need to do between components. Data will flow down from this updated store to any components that need it. Because all of the components that I built with the exception of my forms were dependent on each other for data, I used a state management tool.

class AccountContainer extends Component {
    componentDidMount() {
        this.props.getAllAccounts();
    }

    //can call lifecycle hooks
    //render stated component
    //return react element from render function

    render() {
        console.log(this.props.accounts);

        return (
            <div>
                <Switch>
                    <Route exact path="/accounts/new" component={AccountNew} />
                    <Route
                        exact
                        path="/accounts/:id"
                        render={(props) => {
                            console.log(this.props.accounts);
                            console.log(this.props.account);
                            return (
                                <Account
                                    {...props}
                                    account={this.props.account}
                                />
                            );
                        }}
                    />
                    <Route
                        exact
                        path="/accounts"
                        render={(props) => {
                            return (
                                <Accounts
                                    {...props}
                                    accounts={this.props.accounts}
                                />
                            );
                        }}
                    />
                </Switch>
            </div>
        );
    }
}
//selects part of data from the store that the component needs. receives entire store, returns object
//is this needed if not displaying list of accounts?
const mapStateToProps = (state) => {
    //subscribe to redux updates
    //this is the state from redux
    return {
        account: state.loginFormReducer, //accounts located inside the state
        accounts: state.accounts,
    };
};
//dispatch happens automatically with connect
export default connect(mapStateToProps, { getAllAccounts })(AccountContainer);
Enter fullscreen mode Exit fullscreen mode

I used local state for my forms because I only needed to display what the user was entering into the form back to the user on the page. This state data was then passed to an action, followed by a reducer which then updates the global state.

class AccountNew extends React.Component {
  state = { name: "", balance: "" };

  onChange = (event) => {
    this.setState({
      //do not directly set state, can accept a function to display most up to date value
      [event.target.name]: event.target.value,
    });
  };

  handleSubmit = (event) => {
    event.preventDefault();
    this.props.newAccount(this.state);
    this.setState({
      name: "",
      balance: "",
    });
  };

  render() {
    return (
      <div>
        <form onSubmit={this.handleSubmit}>
          <label>Account Name: </label>
          <input
            type="text"
            placeholder="Name"
            value={this.state.name}
            name="name"
            onChange={this.onChange}
          />
          <br />
          <label>Account Balance: </label>
          <input
            type="text"
            placeholder="Balance"
            value={this.state.balance}
            name="balance"
            onChange={this.onChange}
          />
          <br />
          <input type="submit" />
        </form>
      </div>
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

Aside from these examples of global and local state, I persisted some data in localStorage which is an object that stores a string with no expiration time. This data stated that the user was logged in and was used by logic throughout different components to display different messages to the user based on login status.

In App Component - Creating a const with the value from localStorage

import React from "react";
import { Switch, Route, Redirect, withRouter } from "react-router-dom";
import { connect } from "react-redux";
import AccountContainer from "./containers/AccountContainer";
import NavBar from "./components/NavBar.js";
import DepartmentsContainer from "./containers/DepartmentsContainer";
import PaymentsContainer from "./containers/PaymentsContainer";
import Login from "./components/registrations/Login";
import Signup from "./components/registrations/Signup";
import "./App.scss";

function App(props) {
  const currentAccount = localStorage.getItem("loggedIn");
  return (
Enter fullscreen mode Exit fullscreen mode

Storing LoggedIn value in Account Action when User logs in:

export const getAccount = (data, history) => {
  //a thunk
  return (dispatch) => {
    console.log(data.relationships.account.data.id);
    return fetch(
      `http://localhost:3001/api/v1/accounts/${data.relationships.account.data.id}`,
      {
        method: "GET",
        credentials: "same-origin",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify(),
      }
    )
      .then((res) => res.json())
      .then((account) => {
        if (account.error) {
          console.log(account);
          alert("error");
        } else {
          console.log(account.data.id);
          localStorage.setItem("loggedIn", true); //can only set string, JSON.stringify to convert
          dispatch(setCurrentAccount(account.data));
          history.push(`/accounts/${account.data.id}`);
        }
      })
      .catch(console.log);
  };
};
Enter fullscreen mode Exit fullscreen mode

Discussion (0)