Categories
JavaScript React

Intro to React State Management with React-Redux

React is a library for creating front end views. It has a big ecosystem of libraries that work with it. Also, we can use it to enhance existing apps.

To store data in a central place for easy accessibility by components, we have to use some state management solutions. React-Redux is a popular choice.

In this article, we’ll look at how to add it to our React app and simple use cases.

Installation

To install the react-redux package, we have install react-redux and its dependency redux .

We can install both by running:

npm install react-redux redux

with NPM or if we use Yarn:

yarn add react-redux redux

Set Up a Redux Store

After installing both packages, we have to set up our Redux store to hold our data.

To do this we write:

import { createStore } from "redux";

function counterReducer(state = 0, action) {  
  switch (action.type) {  
    case "INCREMENT":  
      return state + 1;  
    default:  
      return state;  
  }  
}

const store = createStore(counterReducer);

The code above creates a Redux store by creating the counterReducer reducer function.

The reducer specifies how the app’s state changes in response to actions sent to the store.

Our counterReducer only accepts one action, which is 'INCREMENT' . We respond to the action by returning the state and adding 1 to it.

There’s also a default case to just return the state value as is.

Then we create a store by calling Redux’s createStore function and passing in our reducer.

It returns the store , which we can pass into our React app.

Connecting the Store to our React App

This is where we need the functions of React-Redux.

We can connect the store to our app so we can store our app’s state in the store by using React-Redux’s connect function.

connect takes 2 functions, which are mapStateToProps and mapDispatchToProps . They’re the first and second argument respectively.

First, we can put the whole app together by connecting our store with the React app as follows:

import React from "react";  
import { Provider, connect } from "react-redux";  
import { createStore } from "redux";  
import ReactDOM from "react-dom";

function counterReducer(state = 0, action) {  
  switch (action.type) {  
    case "INCREMENT":  
      return state + 1;  
    default:  
      return state;  
  }  
}

const store = createStore(counterReducer);

class App extends React.Component {  
  onClick() {  
    this.props.increment();  
  }  
  render() {  
    const { count } = this.props;  
    return (  
      <>  
        <button onClick={this.onClick.bind(this)}>Increment</button>  
        <p>{count}</p>  
      </>  
    );  
  }  
}

const mapDispatchToProps = dispatch => {  
  return {  
    increment: () => dispatch({ type: "INCREMENT" })  
  };  
};  
const mapStateToProps = state => ({  
  count: state  
});App = connect(  
  mapStateToProps,  
  mapDispatchToProps  
)(App);  
const rootElement = document.getElementById("root");  
ReactDOM.render(  
  <Provider store={store}>  
    <App />  
  </Provider>,  
  rootElement  
);

In the code, we have an app that shows a number going up as we click the Increment button.

The first step to connect the store to our React app is to wrap the Provider component around our whole app.

To do this we, wrote the following:

<Provider store={store}>  
  <App />  
</Provider>

Then we define the mapStateToProps and mapDispatchToProps functions as follows:

const mapDispatchToProps = dispatch => {  
  return {  
    increment: () => dispatch({ type: "INCREMENT" })  
  };  
};  
const mapStateToProps = state => ({  
  count: state  
});

In mapStateToProps we return a object to map the state object to a React component prop name as indicated in the property name. The state is the state stored in the Redux store.

So we mapped state to the prop count .

In mapDispatchToProps , we get the dispatch parameter, which is a function used to dispatch our action to the store, and return an object with the name for the function we can access from the props to call and dispatch the action.

So in mapDispatchToProps , increment is our function name. We can call it by running this.props.increment in our App component.

increment is a function that runs dispatch({ type: “INCREMENT” }) .

Then in our App component, we define the onClick method to call this.props.increment to dispatch the ‘INCREMENT’ action to our counterReducer via our Redux store and increase the state by 1.

Then since we have mapStateToProps , the latest value is being observed by App and the latest value is available via this.state.count as indicated in mapStateToProps .

Then when we click the Increment button, we’ll get the number going up by 1.

This will happen with every click.

Conclusion

We can use Redux in our React app by creating a store.

Next, we use the React-Redux Provider component and wrap it around our App entry-point component. We pass our Redux store to the store prop so that we can map the state and dispatch actions to props.

Then connecting it to our store via React Redux’s connect function. We pass in the mapStateToProps and mapDispatchToProps to map the store’s state to our component’s props and map our dispatch function call to our props respectively.

Categories
JavaScript React

Reacting to Observables with MobX-React

We can use MobX with MobX-React to manage the state by watching MobX observables and changing the observable data.

In this article, we’ll look at how to create an Observable and use it in React components directly.

Creating Observables and Using Them in React Components

We have to install the mobx and mobx-react packages to create Observables and use them within React components.

To do this, we can write the following:

npm i mobx mobx-react

Then we can create our Observable object and a React component that uses it as follows:

import React from "react";  
import ReactDOM from "react-dom";  
import { observer } from "mobx-react";  
import { observable } from "mobx";

const countData = observable({  
  count: 0  
});

const App = observer(({ countData }) => (  
  <>  
    <button onClick={() => countData.count++}>Increment</button>  
    <p>{countData.count}</p>  
  </>  
));  
const rootElement = document.getElementById("root");  
ReactDOM.render(<App countData={countData} />, rootElement);

In the code above, we created the countData Observable object by writing:

const countData = observable({  
  count: 0  
});

We’ll be able to get the latest state within our React component and then pass it in as a prop for our React component.

To make our React component watch for the latest value from our Observable object and let us change its value from within the component, we write:

const App = observer(({ countData }) => (  
  <>  
    <button onClick={() => countData.count++}>Increment</button>  
    <p>{countData.count}</p>  
  </>  
));  
const rootElement = document.getElementById("root");  
ReactDOM.render(<App countData={countData} />, rootElement);

The code above gets the countData prop from the countData Observable. Then in the onClick handler, we just pass in the function to change its value.

Then when we reference the App component in the last line, we just pass in the countData Observable object as the value of the countData prop.

countData.count++ will change the state of the Observable, which then the latest value will be obtained from the prop.

We can use the countData Observable object with class components as follows:

import React from "react";  
import ReactDOM from "react-dom";  
import { observer } from "mobx-react";  
import { observable } from "mobx";const countData = observable({  
  count: 0  
});

@observer  
class App extends React.Component {  
  render() {  
    return (  
      <>  
        <button onClick={() => countData.count++}>Increment</button>  
        <p>{countData.count}</p>  
      </>  
    );  
  }  
}

const rootElement = document.getElementById("root");  
ReactDOM.render(<App countData={countData} />, rootElement);

The only difference is that it’s a class component with a render method and that we use the observer decorator instead of the observer function.

Using Context to Pass Observables Around

We can use the React Context API to pass the values of an Observable to another component.

For instance, we can write the following code:

import React, { useContext } from "react";  
import ReactDOM from "react-dom";  
import { observer } from "mobx-react";  
import { observable } from "mobx";

const countData = observable({  
  count: 0  
});

const CountContext = React.createContext();

const Counter = observer(() => {  
  const countData = useContext(CountContext);  
  return (  
    <>  
      <button onClick={() => countData.count++}>Increment</button>  
      <p>{countData.count}</p>  
    </>  
  );  
});

const App = ({ countData }) => (  
  <CountContext.Provider value={countData}>  
    <Counter />  
  </CountContext.Provider>  
);

const rootElement = document.getElementById("root");  
ReactDOM.render(<App countData={countData} />, rootElement);

In the code above, we have the CountContext . We create it by writing:

const CountContext = React.createContext();

Then in App , we included the CountContext component, so that we can get the countData Observable value by passing in the countData prop with the value set to the countData Observable.

In the Counter component, we call the observer function with a function component inside.

Inside the component, we use the useContext hook to get the value from CountContext .

Then in the handler function, we passed into the onClick prop of the button, we changed the count ‘s value, which will automatically change the value in the countData Observable object and set as the value of countData.count .

Therefore, when we click the Increment button, the number below it will go up.

Storing Observables in Local Component State

We can also use Observables as a local state in a React component.

To do this, we can use the useState hook by passing in a function that returns an Observable object as follows:

import React, { useState } from "react";  
import ReactDOM from "react-dom";  
import { observer } from "mobx-react";  
import { observable } from "mobx";const App = observer(() => {  
  const \[countData\] = useState(() =>  
    observable({  
      count: 0  
    })  
  ); return (  
    <>  
      <button onClick={() => countData.count++}>Increment</button>  
      <p>{countData.count}</p>  
    </>  
  );  
});  
const rootElement = document.getElementById("root");  
ReactDOM.render(<App />, rootElement);

In the code above, we used the useState hook by passing in a function that returns an Observable object with the count property. We then assigned that to the countData variable.

Once we did that, countData.count will be updated by the onClick handler and when we click the Increment button, we’ll see the countData.count value update automatically.

If we click the Increment button, then the value will go up.

We don’t really need to use MobX Observable objects to store local state unless complex computations are involved, since MobX will optimize for those.

Likewise, we can use MobX to store local state with class components as follows:

import React, { useState } from "react";  
import ReactDOM from "react-dom";  
import { observer } from "mobx-react";  
import { observable } from "mobx";@observer  
class App extends React.Component {  
  @observable count = 0; render() {  
    return (  
      <>  
        <button onClick={() => this.count++}>Increment</button>  
        <p>{this.count}</p>  
      </>  
    );  
  }  
}  
const rootElement = document.getElementById("root");  
ReactDOM.render(<App />, rootElement);

In the code above, we have this.count , which is an Observable field. In the onClick listener, we just modify this.count directly, and then it’ll be reflected in the this.count in between the p tag.

@observer implements memo orshouldComponentUpdate automatically so that there won’t be any unnecessary re-renders.

Conclusion

We can use MobX Observable to store a React app’s state or a React component’s state.

To use it for storing React app’s state, we can either pass an Observable to a component as a prop or we can use the Context API to send the data to different components.

We can also use it to store local state by either using the useState hook in function components and using the observable decorator within a class component.

Categories
JavaScript React

Defining Nested Routes with React Router

React is a library for creating front end views. It has a big ecosystem of libraries that work with it. Also, we can use it to enhance existing apps.

To build single-page apps, we have to have some way to map URLs to the React component to display.

In this article, we’ll look at how to define nested routes with React Router.

Nested Routes

To define nested routes, first, we define a grandchild route to display the content of the nested routes. We can use the useParams hook to get any route parameters.

Then we can define nested routes by defining a child component to hold our nested routes.

In the child component, we get the url and path properties from the useRouteMatch hook.

Then we use the url variable to prefix the path in the to prop of the Link s. In the Route components, we prefix the path prop with the path property returned from useRouteMatch .

Then we create a parent component that holds the child route that we defined above.

For example, we can define them as follows:

import React from "react";  
import ReactDOM from "react-dom";  
import {  
  BrowserRouter as Router,  
  Switch,  
  Route,  
  Link,  
  useParams,  
  useRouteMatch  
} from "react-router-dom";

function Topic() {  
  let { topicId } = useParams(); return (  
    <div>  
      <h3>{topicId}</h3>  
    </div>  
  );  
}

function Topics() {  
  let { path, url } = useRouteMatch(); return (  
    <div>  
      <h2>Topics</h2>  
      <ul>  
        <li>  
          <Link to={`${url}/foo`}>Foo</Link>  
        </li>  
        <li>  
          <Link to={`${url}/bar`}>Bar</Link>  
        </li>  
        <li>  
          <Link to={`${url}/baz`}>Baz</Link>  
        </li>  
      </ul> <Switch>  
        <Route exact path={path}>  
          <h3>Please select a topic.</h3>  
        </Route>  
        <Route path={`${path}/:topicId`}>  
          <Topic />  
        </Route>  
      </Switch>  
    </div>  
  );  
}

function App() {  
  return (  
    <Router>  
      <div>  
        <ul>  
          <li>  
            <Link to="/">Home</Link>  
          </li>  
          <li>  
            <Link to="/topics">Topics</Link>  
          </li>  
        </ul> <hr /> <Switch>  
          <Route exact path="/">  
            <p>Home</p>  
          </Route>  
          <Route path="/topics">  
            <Topics />  
          </Route>  
        </Switch>  
      </div>  
    </Router>  
  );  
}const rootElement = document.getElementById("root");  
ReactDOM.render(<App />, rootElement);

On the top of the code above, we have the Topics component to display the content of the nested route. We get the id URL parameter with useParams .

The Route that renders Topic has the path of /topics/:topicId . :topicId indicates a placeholder that we can get from useParams .

Then we have the Topics component, which is the parent of the Topic component. It has the child routes as defined in the following code:

<Switch>  
    <Route exact path={path}>  
        <h3>Please select a topic.</h3>  
    </Route>  
    <Route path={`${path}/:topicId`}>  
        <Topic />  
    </Route>  
</Switch>

In the Route we nest Topic in it so we display whatever’s returned there.

The path is set to path={`${path}/:topicId`} so that we call useRouteMatch to prefix the path with what we’ll define in App . That means path will be set to /topics as we’ll do later.

We also have the Link component’s to props prefixed with the url so that they’ll go to the right URL when we click on the links.

Then in App , we have:

<Switch>  
    <Route exact path="/">  
        <p>Home</p>  
    </Route>  
    <Route path="/topics">  
        <Topics />  
    </Route>  
</Switch>

to set the top-level routes. As we mentioned before, we’ll set the top-level route to topics as we did in the second Route component.

In the Link components, we set the relative paths to go to when we click on them.

Then we get something like the following when we click on the links:

Conclusion

To define nested routes, we have to nest Route objects within each other.

The cleanest way to do this is to split each level of nesting into their own components.

We can use the useRouteMatch hook to return the path, and url . The path is for prefix the path prop of Route s, and url is for prefixing the to prop of Links of nested routes.

Categories
JavaScript React

Built-in React Hooks —useLayoutEffect and useDebugValue

React is a library for creating front end views. It has a big ecosystem of libraries that work with it. Also, we can use it to enhance existing apps.

In this article, we look at the useLayoutEffect and useDebugValue hooks.

useLayoutEffect

useLayoutEffect has an identical signature to useEffect , but it fires synchronously after all DOM mutations are done.

This is useful for reading layout from the DOM and synchronously re-render.

Updates inside useLayoutEffect will be flushed synchronously before the browser has a chance to paint.

To prevent blocking visual updates, we should use useEffect whenever possible.

useLayoutEffect fires in the same phase as componentDidMount and componentDidUpdate .

We should only use useLayoutEffect if useEffect isn’t working properly.

If server rendering is used, the useLayoutEffect nor useEffect are run until the JavaScript is downloaded.

React warns when server-rendered component contains useLayourEffect .

To exclude components that need layout effects from server-rendered HTML, we render it conditionally with showChild && <Child /> and defer showing it with useEffect(() => { setShowChild(true); }, []).

Then the UI won’t appear broken before rendering is done.

For example, we can render synchronous with useLayoutEffect as follows:

function App() {  
  const [value, setValue] = React.useState(0);  
  React.useLayoutEffect(() => {  
    if (value === 0) {  
      setValue(10 + Math.random() * 200);  
    }  
  }, [value]); return (  
    <>  
      <button onClick={() => setValue(0)}>Random Value</button>  
      <p>{value}</p>  
    </>  
  );  
}

Since value is set initially to 0, we want to update that first before rendering, so we use useLayoutEffect hook and call setValue in the callback.

Also, since we set value to 0 when clicking the button, we want to only show the value when value when it’s not 0, so useLayoutEffect will prevent 0 from being shown.

useDebugValue

useDebugValue is used to display a label for custom hooks in React DevTools.

It takes any value that we want to display.

For example, we can use it as follows:

import React, { useEffect, useState } from "react";  
import ReactDOM from "react-dom";  
const useDelayedMessage = (msg, delay) => {  
  const [message, setMessage] = useState("");  
  useEffect(() => {  
    setTimeout(() => {  
      setMessage(msg);  
    }, delay);  
  });  
  React.useDebugValue(message ? "Message Set" : "Message not set");  
  return message;  
};

function App() {  
  const delayedMessage = useDelayedMessage("foo", 1500);  
  return <div>{delayedMessage}</div>;  
}

Since we have:

React.useDebugValue(message ? "Message Set" : "Message not set");

in React DevTools, we’ll see the Message not set message when the page first loads, and then when message is set after the delay , then we see Message set .

This hook is useful for custom hooks that are part of shared libraries.

Defer formatting debug values

In some cases formatting, a value for display may be an expensive operation.

Therefore, useDebugValue accepts a formatting function as an optional second parameter. The function is only called if the hooks are inspected.

It receives the debug value as a parameter and returns the formatted display value.

For example, we can write:

import React, { useEffect, useState } from "react";  
import ReactDOM from "react-dom";  
const useDelayedMessage = (msg, delay) => {  
  const [message, setMessage] = useState("");  
  useEffect(() => {  
    setTimeout(() => {  
      setMessage(msg);  
    }, delay);  
  });  
  React.useDebugValue(message, message =>  
    message ? "Message Set" : "Message not set"  
  );  
  return message;  
};

function App() {  
  const delayedMessage = useDelayedMessage("foo", 1500);  
  return <div>{delayedMessage}</div>;  
}

In the code above, we have:

React.useDebugValue(message, message =>  
    message ? "Message Set" : "Message not set"  
  );

which runs the formatting function in the second argument when it’s inspected.

The message parameter has the same value as message returned from useState .

Conclusion

We can use the useLayoutEffect hook to synchronous run code after all DOM mutations are done.

useDebugValue hook is handy for debugging with React DevTools since it lets us display something in the DevTools console as the hook runs.

Categories
JavaScript React

How to Handle Forms With React

In this article, we’ll look at how to handle forms with React.


Form Handling

The standard way to handle form input value changes is to handle them with React. This is a technique called controlled components.

We can create controlled components with input, textarea, and select elements. They maintain their own state, and updates are based on user input.

Like with anything else, the component state is updated with setState(). This means that we have to call it with the value of the input elements to update the state with the inputted values.

To update the state with the inputted value, we can write the following:

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = { name: "" };
  }
  handleChange(event) {
    this.setState({ name: event.target.value });
  }
  handleSubmit(event) {
    alert(`Name submiited: ${this.state.name}`);
    event.preventDefault();
  }
  render() {
    return (
      <div className="App">
        <form onSubmit={this.handleSubmit.bind(this)}>
          <label>
            Name:
            <input
              type="text"
              value={this.state.name}
              onChange={this.handleChange.bind(this)}
            />
          </label>
          <input type="submit" value="Submit" />
        </form>
      </div>
    );
  }

In the code above, we have the input element that has an onChange handler which is set to this.handleChange. The handleChange method has the code to update this.state.name with the latest inputted value. event.target.value has the latest value.

Then, we attached an onSubmit handler to the form, which is set to this.handleSubmit.

We call event.preventDefault to prevent the default submit action, so we can run JavaScript code in the handler to display the alert box with the latest inputted value.

The handler methods make it easy to handle input validation. We can also manipulate it there. For example, we can change our input value before updating the state as follows:

handleChange(event) {  
  this.setState({name: event.target.value.toLowerCase()});  
}

We don’t have to create an extra function to change our input to lowercase before setting it to this.state.name.

We have the value prop to set the value to the latest state so that we can see our inputted value.


The textarea Tag

Like with handling input elements, we can handle value changes with textarea elements the same way.

We can write the following to set the state to the latest inputted value:

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = { essay: "" };
  }
  handleChange(event) {
    this.setState({ essay: event.target.value });
  }
  handleSubmit(event) {
    alert(`Essay submiited: ${this.state.essay}`);
    event.preventDefault();
  }
  render() {
    return (
      <div className="App">
        <form onSubmit={this.handleSubmit.bind(this)}>
          <label>Essay:</label>
          <br />
          <textarea
            type="text"
            value={this.state.essay}
            onChange={this.handleChange.bind(this)}
          />
          <br />
          <input type="submit" value="Submit" />
        </form>
      </div>
    );
  }
}

It’s pretty much the same as the code for handling inputted values from the input element, but we have a textarea element instead.


The select Tag

The select element creates a drop-down where we can select more values.

We can write the following code to get the selection from the drop-down and set it to the state:

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = { fruit: "apple" };
  }
  handleChange(event) {
    this.setState({ fruit: event.target.value });
  }
  handleSubmit(event) {
    alert(`Favorite fruit: ${this.state.fruit}`);
    event.preventDefault();
  }
  render() {
    return (
      <div className="App">
        <form onSubmit={this.handleSubmit.bind(this)}>
          <label>Favorite Fruit:</label>
          <br />
          <select
            onChange={this.handleChange.bind(this)}
            value={this.state.fruit}
          >
            <option value="apple">Apple</option>
            <option value="orange">Orange</option>
            <option value="mango">Mango</option>
          </select>
          <br />
          <input type="submit" value="Submit" />
        </form>
      </div>
    );
  }
}

As we can see, it’s almost the same as the other examples, except that we have a select element with option elements inside instead.

select elements also work for getting multiple selections. We can update the state with all the selections by getting the options available, getting the ones that are selected, and putting them in an array as follows:

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = { fruits: [] };
  }
  handleChange(event) {
    const options = event.target.options;
    const fruits = [];
    for (const option of options) {
      if (option.selected) {
        fruits.push(option.value);
      }
    }
    this.setState({ fruits });
  }
  handleSubmit(event) {
    alert(`Favorite fruit: ${this.state.fruits.join(",")}`);
    event.preventDefault();
  }
  render() {
    return (
      <div className="App">
        <form onSubmit={this.handleSubmit.bind(this)}>
          <label>Favorite Fruit:</label>
          <br />
          <select
            multiple
            onChange={this.handleChange.bind(this)}
            value={this.state.fruits}
          >
            <option value="apple">Apple</option>
            <option value="orange">Orange</option>
            <option value="mango">Mango</option>
          </select>
          <br />
          <input type="submit" value="Submit" />
        </form>
      </div>
    );
  }
}

In the code, we have:

handleChange(event) {
  const options = event.target.options;
  const fruits = [];
  for (const option of options) {
    if (option.selected) {
      fruits.push(option.value);
    }
  }
  this.setState({ fruits });
}

In the first line of the function, we have:

const options = event.target.options;

Which has all the options from the select element.

Then, we can loop through the options and check the selected property of each and then push them to the array if selected is true. We then call setState with fruits to update the state with the latest selected values.

In handleSubmit, we can this.state.fruits and call join on it with a comma to convert it to a comma-separated string.

Also, the value prop of the select element takes an array instead of a string. React is smart enough to parse the array and render the selected values.


Handling Multiple Inputs

When we need to handle multiple inputs, we don’t want to make a new onChange handler function for each input. Therefore, we want to make a function that can set all values.

We can do that as follows:

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = { firstName: "", lastName: "" };
  }
  handleChange(event) {
    const target = event.target;
    const value = target.value;
    const name = target.name;
    this.setState({
      [name]: value
    });
  }
  handleSubmit(event) {
    alert(`Name ${this.state.firstName} ${this.state.lastName}`);
    event.preventDefault();
  }
  render() {
    return (
      <div className="App">
        <form onSubmit={this.handleSubmit.bind(this)}>
          <label>First Name:</label>
          <br />
          <input
            name="firstName"
            onChange={this.handleChange.bind(this)}
            value={this.state.firstName}
          />
          <br />
          <label>Last Name:</label>
          <br />
          <input
            name="lastName"
            onChange={this.handleChange.bind(this)}
            value={this.state.lastName}
          />
          <br />
          <input type="submit" value="Submit" />
        </form>
      </div>
    );
  }
}

In the code above, we have the following handleChange method:

handleChange(event) {
  const target = event.target;
  const value = target.value;
  const name = target.name;
  this.setState({
    [name]: value
  });
}

We get the event.target object and set it to target. The target has the name and value properties that have the input’s name and value attribute values respectively.

Therefore, we can take advantage of ES6’s dynamic object property feature and call setState with the name as the property name and value as the value to update the state.


Conclusion

We should handle input changes by writing controlled components. To do this, we attach an event handler function to the onChange event.

To handle form submit, we attach an onSubmit event handler to the form, and then get the event object from the parameter and call event.preventDefault inside so we can run JavaScript code in the handler.

To handle multiple input changes with one onChange handler, we get the name and value properties from event.target from the handler’s parameter and update it with the dynamic property name feature available since ES6.