React Ionic Framework and Hooks

Aaron K Saunders - Sep 24 '19 - - Dev Community

Please checkout and subscribe to my video content on YouTube. Feel free to leave comments and suggestions for what content you would like to see.
YouTube Channel

Overview

Simple application with a list of things and the ability to add, edit and delete things. We will use the useReducer hook to manage the state of the array of things.

We will use the useState hook to manage the state of the modal dialog we are using to input the information for the thing we are editing or updating and we use the useState hook to manage the state of the input field in the modal dialog.

Lets Start with the useReducer API

// useThings.js
// --
import React from "react";

const useThings = () => {
  // handle the specific action dispatched
  const reducer = (state, action) => {
    switch (action.type) {
      case "ADD_THING": { }
      case "DELETE_THING": { }
      case "EDIT_THING": { };
      default: {
        return state;
      }
    }
  };

  // here we set things up to use the reducer
  const [state, dispatch] = React.useReducer(reducer, {
    things: []
  });

  // the function returns everything needed to the caller to
  // dispatch specific action and get the updated state changes
  return {
    state,
    dispatch
  };
};

export default useThings;
Enter fullscreen mode Exit fullscreen mode

Modify values in the state

Add An Item: Add the action.data to the end of the array, set state properties

case "ADD_THING": {
  return { ...state, things: [...state.things, action.data] };
}
Enter fullscreen mode Exit fullscreen mode

Deleting An Item: Add the action.index slice the array to get the things before the thing specified by the index and everything after the item specified by the index. This in turn is used to create new array which we set state.things with

case "DELETE_THING": {
  return {
    ...state,
    things: [
      ...state.things.slice(0, action.index),
      ...state.things.slice(action.index + 1)
    ]
  };
}
Enter fullscreen mode Exit fullscreen mode

Editing An Item: Add the action.index slice the array to get the things before the thing specified by the index and everything after the item specified by the index. Next we use the action.data as the new element to replace the element that was previously there. This in turn is used to create new array which we set state.things with.

case "EDIT_THING": {
  return {
    ...state,
    things: [
      ...state.things.slice(0, action.index),
      action.data,
      ...state.things.slice(action.index + 1)
    ]
  };
}
Enter fullscreen mode Exit fullscreen mode

Displaying a Modal for User Input

Alt Text

Using the useState functionality to manage displaying the modal
dialog for inputting data for new things or editing things. The state has two keys, isVisible and value. isVisible will be set to true to show the dialog and false to hide it. The value property will be set when we are actually editing an object. We will also add an additional property called index when editing a thing so we can find it in the state array to update it.

// ThingsList.js
// --
// using the useState functionality to manage displaying the modal
// dialog for inputting data for new things or editing things
const [modalInfo, setModalInfo] = useState({ isVisible: false, value: "" });
Enter fullscreen mode Exit fullscreen mode

Managing the Input Value Using useState

// ThingEdit.js
// --
const [inputValue, setInputValue] = useState();
Enter fullscreen mode Exit fullscreen mode

How we use this in the render method of the component; when there is an input event in the input element, we update the state with the value entered by the user

<IonInput
  value={inputValue}
  onInput={e => setInputValue(e.target.value)} />
Enter fullscreen mode Exit fullscreen mode

So when the user is finished in the modal they will click on of two buttons to call the handleClick method

<IonButton onClick={() => handleClick(true)}>Save</IonButton>
<IonButton onClick={() => handleClick(null)}>Cancel</IonButton>
Enter fullscreen mode Exit fullscreen mode

If handleClick is called with a true value, then we need to return the value from the input form which is saved in our state, if the value is passed to handleClick is null, then we just need to exit the function and not return any data

// ThingEdit.js
// --
const handleClick = _save => {
  handleFormSubmit({ isVisible: false, value: _save && inputValue });
};
Enter fullscreen mode Exit fullscreen mode

Back in the ThingsList component we need to handle the call from the ThingEdit component to process the data received from the modal.

Get the response from the modal/form so we can update or create a new item. if the _formResponse.value is empty then ignore because the user selected the cancel button.

If there is a _formResponse.value & modalInfo.index has a value, then
edit the item; the modalInfo.index variable tells us which item in t he array to update; if no modalInfo.index then create a new things with the _formResponse.value

// ThingsList.js
// --
const handleFormSubmit = _formResponse => {
  if (_formResponse.value) {
    modalInfo.index != null
      ? editEntry(modalInfo.index, _formResponse.value)
      : addNewEntry(_formResponse.value);
  }
  // reset the modalInfo state
  setModalInfo({ ...modalInfo, isVisible: false, value: "" });
};
Enter fullscreen mode Exit fullscreen mode

Displaying the List Of Things

Alt Text
Rendering the list of things from the components custom hook, useThings, we mentioned at the start of the post.

// get the function from my custom hook to mange the list
// of things
let { state, dispatch } = useThings();
Enter fullscreen mode Exit fullscreen mode

This give us access to the state object and the state object contains state.things. We loop through the array of values using the Array.map() function

<IonList>
  {state.things.map((_thing, _index) => (
    <IonItem key={_index}>
      <IonLabel className="ion-text-wrap">{_thing}</IonLabel>
      <IonButton onClick={() => modalInfoWithEntry(_thing, _index)}>
        Edit
      </IonButton>
      <IonButton color="danger" onClick={() => deleteEntry(_index)}>
        Delete
      </IonButton>
    </IonItem>
  ))}
</IonList>
Enter fullscreen mode Exit fullscreen mode

We have all of the base function that are wrappers for calling the reducer methods with dispatch

// ThingsList.js
//- 

/**
 * add entry to the list using `dispatch` from custom hook
 */
const addNewEntry = _data => {
  dispatch({ type: "ADD_THING", data: _data });
};

/**
 * remove entry from the list using `dispatch` and index in the array
 * to call custom hook
 * @param {*} _index
 */
const deleteEntry = _index => {
  dispatch({ type: "DELETE_THING", index: _index });
};

/**
 * update an existing entry in the list based on data
 * and the index of the entry
 * @param {*} _index
 * @param {*} _data
 */
const editEntry = (_index, _data) => {
  let payload = { index: _index, data: _data };
  dispatch({ type: "EDIT_THING", ...payload });
};

Enter fullscreen mode Exit fullscreen mode

Wrapping It All Up

All of the code for this projects in available to you here in the CodeSandbox.io website listed below.

React hooks with useState and useReducer allows for your whole application to just be functional components who's state can be managed with the hooks api.

Here is a link to a great video to give you some of the reasons why you might want to give hooks a try in your application.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .