Web Development

Data-Driven Functional Components via Ajax Featuring Webhooks and Redux

March 11th, 2020 | By Camilo Reyes | 4 min read

Introducing React Hooks New Data-Driven Components: Once upon a time, class components were the only way to hook into lifecycle methods. This, for example, allowed the component to load initial data via Ajax on the mount.

The lifecycle method componentDidMount worked well for this use case. This forced the component to manage its state and did not come without faults.

One gotcha was being able to manage the state with Redux. Reloading component data without ugly hacks was challenging. This difficulty was because it was doing state management in two places: the component itself and in Redux. As class component complexity grew, so did the hacks.

Enter Hooks, a new way to manage state without class components during initial load.

The idea is to let the Redux store manage the state without lifecycle methods. This allows code reuse because state management gets encapsulated in the store. When dispatch actions fire, the stored state has everything to know about each component.

Dive into Hooks to tackle this use case.

Loading initial component data while letting Redux manage the state has its benefits. We will skip setting up a new code project to keep the focus on relevant code. All code samples are on the GitHub repository.

Redux State to Props

At the initial load, there are three props we care about isInit, isLoading, and isError.

The init prop signals to the component that it is time to begin loading data. While Ajax is waiting for a response, isLoading can show a user-friendly loader in the UI. Then, if there are any errors, isError puts the component in an error state.

Therefore, the initial state in Redux can be:

const initialState = {
  firstName: '',
  isInit: false,
  isLoading: false,
  isError: false
};


We appended firstName to the state since it is the data coming back as the response. Because Redux encourages a single store, this response data can live anywhere. For example, a parent component can manage the loading state, while a child component encapsulates response data.

To keep this example simple, we put it in the same hierarchy.

Redux-Thunk

These three props act like control flags. The goal is to support every event that happens during load. To flip each flag, create these action types:

const DUMMY_LOADING_STATE_DATA = 'DUMMY_LOADING_STATE_DATA';
const DUMMY_UPDATE_STATE_DATA = 'DUMMY_UPDATE_STATE_DATA';
const DUMMY_ERROR_STATE_DATA = 'DUMMY_ERROR_STATE_DATA';


Fire dispatch messages across the store by creating action functions with a payload or error:

export const showLoadingState = () => ({type: DUMMY_LOADING_STATE_DATA});
export const updateStateData = (state) => ({type: DUMMY_UPDATE_STATE_DATA, payload: state });
export const errorStateData = (reason) => ({type: DUMMY_ERROR_STATE_DATA, payload: reason, error: true })


Then, put in place the Redux-Thunk that begins the initial load:

const loadInitStateData = () => async (dispatch, getState, axios) => {
  dispatch(showLoadingState());

  try {
    const url = '/person.json';
    const response = await axios.get(url);

    return dispatch(updateStateData(response.data));
  } catch (reason) {
    return dispatch(errorStateData(reason.message));
  }
};

Note the dispatch showLoadingState; this signals the component to wait for response data.

On success, the payload has an Ajax response. On failure, the reason is an error message with an error flag. This is a useful pattern for sending dispatches in Redux.

Dispatch actions that fire across the store have the following properties:

  • type: Required. Message event taking place during load.

  • payload: Optional. Object with response data on success or an error message on failure.

  • error: Optional. A boolean that, when true, says the payload contains an error message.


Finally, all the reducer must do is manage the loading state.

const dummyReducer = (state = initialState, action) => {
  switch (action.type) {
    case DUMMY_LOADING_STATE_DATA:
      return {...state, isLoading: true, isInit: true};

    case DUMMY_UPDATE_STATE_DATA:
      const {firstName} = action.payload;
      return {...state, firstName: firstName, isLoading: false};

    case DUMMY_ERROR_STATE_DATA:
      return {...state, isError: true, isLoading: false};

    default:
      return state;
  }
};


The initialState is set so the component can begin loading. Redux persists in the isInit state once this control flag flips to true, so it knows not to reload. This is because Redux works like a state machine.

The spread ...state maintains previous state data in the store. One gotcha is to be sure to set isLoading to false on errors. This is to prevent the component from appearing frozen in the UI. Customers might get the wrong idea when components never load and do not land in a proper error state.

Functional Component

With all the hard work done in the Redux store, the component can focus on a single concern:

const DummyComponent = ({isInit, isError, isLoading, loadInitStateData, firstName}) =>
  <>
    {useEffect(() => {!isInit && loadInitStateData()})}
    {isLoading && (<p>Loading...</p>)}
    {isError && (<p>An error occurred.</p>)}
    {!isLoading && !isError && isInit && (<p>Hello {firstName}.</p>)}
  </>;


Each control flag flips certain pieces of the UI.

The props drive behavior depending on how each is set in the object parameter. This makes the component testable because it follows a functional paradigm.

Each permutation of inputs has a one-to-one relationship with the rendered output. Note the use of useEffect. This is a Hook that executes at re-render, but notice the dispatch is behind a control flag. This gives control back to Redux so it can do its job, which is to manage the state.

Be sure not to wrap Hooks around any conditional logic.

React catches this during rendering and fails to mount the component. A better idea is to put any control flags inside the callback. This is because functional components must execute Hooks at re-render.

Conclusion

Hooks are an exciting new way to work with functional components.

This is one way React components embrace the functional paradigm. With a Redux store, Hooks levels up because it benefits from this state machine. By letting Redux manage the state, it keeps the separation of concerns and clean code.

As a closing note, pay special attention to JavaScript source code protection if you're developing commercial or enterprise apps. You can do this by starting a free Jscrambler trial, and don't miss our guide for protecting React apps.

Jscrambler

The leader in client-side Web security. With Jscrambler, JavaScript applications become self-defensive and capable of detecting and blocking client-side attacks like Magecart.

View All Articles

Must read next

Web Development

Add a Search Bar Using Hooks and FlatList in React Native

FlatList is a component of the React Native API that allows fetching and displaying large arrays of data. In this tutorial, we use it to add a search bar.

August 28, 2020 | By Aman Mittal | 8 min read

Web Development

Introducing React Hooks

Hooks are surely one of the most welcomed new features to React in a long time. Let's explore different hooks and see how they work.

August 7, 2019 | By Ahmed Bouchefra | 6 min read

Section Divider