Tag Archive

Tag Archives for " react "

Add Hooks to Class Components With Higher-Order Components

If you are using class components, but want to use hooks, you can’t do so directly. One strategy for adding them is to use adapter components. Another strategy is to use higher-order components to pass along data from hooks as props.

Example: Creating a MessageStore

Here’s our sample custom hook: We want to build a flash messaging system, similar to what’s provided by Ruby on Rails.

const useMessageStore = () => {
  const messageReducer = (state, action) => {
  switch (action.type) {
    case 'ADD':
      return [
        ...state,
        action.payload,
      ]
    case 'CLEAR':
      return []
    case 'DISMISS':
      return state.filter((message, index) => index !== action.payload)
    default:
      return state
  }
}
  const [state, dispatch] = useReducer(messageReducer, [])
  return [state, dispatch]
}

Using The Hook in A Functional Component

If we want to display or write messages to our queue from any functional component, we can do so by calling this hook:

const MyMessagingComponent = () => {
  const [messageStore, messageDispatch] = useMessageStore()
  return messageStore.map(message => <span>message.text</span>)
}

And we want to add a message from a function component, we can do it like this:

const MyButton = () => {
  const [messageStore, messageDispatch] = useMessageStore()
  const sendClickMessage = () => { 
    messageDispatch({ type: 'ADD', payload: 'Clicked the button'})
  }
  return (<button onClick={sendClickMessage}>Click Me!</button>)
}

Creating a HOC With Our Hook

Alternatively, we could create a component that passes the data from the hook along:

const withMessageStore = (WrappedComponent) => (props) => {
  const { state, dispatch } = useContext(MessageContext)
  return (<WrappedComponent
    {...props}
    messageStore={state}
    messageDispatch={dispatch}
  />)
}

Apply the HOC to Class Components

Using this method, we can now use our hooks in existing code, without having to refactor the whole thing:

class OldStodgyComponent extends React.Component {
  render() {
    return this.props.messageStore.map(message => <span>message.text</span>)
  }
}

export default withMessageStore(OldStodgyComponent)

Redux Now Has Hooks. A Before and After Comparison

Today the react-redux team released version 7.1.0, which adds hooks to react-redux! Here’s a quick comparison of how it could change how you write components.

First, A Brief Overview of The New Toys

  • useSelector: Pass in a function that takes the state as an argument and returns a value. Used to get a single value from state. Can act as a replacement for mapStateToProps.
  • useDispatch: returns a reference to the dispatch object. It can act as a replacement for mapDispatchToProps.
  • useStore: returns an instance of the store. Generally not recommended.

Old Example: A Search Form

An example component that stores a query and when a form is submitted to search. I wanted to keep the example simple, so use your imagination for the part where it fetches results.

import React from 'react'
import { connect } from 'react-redux'

const defaultSearchBar = ({ query, updateQuery, handleSearch }) => {
  const handleSubmit = (e) => {
    e.preventDefault()
    handleSearch(query)
  }

  const handleChange = e => updateQuery(e.target.value)
  return (
    <form onSubmit={handleSubmit}>
      <input
        name="search"
        value={query}
        onChange={handleChange}
      />
    </form>
  )
}

const mapStateToProps = state => ({
  query: state.query,
})

const mapDispatchToProps = dispatch => ({
  updateQuery: newQuery => dispatch({ type: 'UPDATE_QUERY', payload: newQuery }),
  handleSearch: newSearch => dispatch({ type: 'NEW_SEARCH', payload: newSearch }),
})

const connectedSearchBar = connect(
  mapStateToProps,
  mapDispatchToProps,
)(defaultSearchBar)

The New Component

In our new example, we have a few differences: We eliminate the connect function, mapStateToProps, and mapDispatchToProps entirely. This means our component no longer takes in props directly. Now, our form looks like this:

import React from 'react'
import { useSelector, useDispatch } from 'react-redux'

const hookedSearchBar = () => {
  const dispatch = useDispatch()
  const query = useSelector(state => state.query)
  const handleSubmit = (e) => {
    e.preventDefault()
    dispatch({ type: 'NEW_SEARCH', payload: query })
  }
  const handleChange = e => dispatch({ type: 'UPDATE_QUERY', payload: e.target.value })

  return (
    <form onSubmit={handleSubmit}>
      <input
        name="search"
        value={query}
        onChange={handleChange}
      />
    </form>
  )
}

Creating Your Own Custom Hooks

If you have code that gets shared frequently between components, you can create a custom hook to keep all of that functionality together. Here’s an example of us isolating the redux-specific part of our form into its own hook:

useSearchQuery = () => {
  const dispatch = useDispatch()
  const query = useSelector(state => state.query)
  const updateQuery = query => dispatch({ type: 'UPDATE_QUERY', payload: query })
  const updateSearch = search => dispatch({ type: 'NEW_SEARCH', payload: search })
  return { query, updateQuery, updateSearch }
}

Should You Make The Switch?

The ability to create redux hooks above is interesting, but I am also concerned that it could make code harder to test, as testing these components is already dead simple. I’m not sold either way, but I hope this comparison makes it easier for you to make informed decisions about your code base.

How to Use useEffect (and other hooks) in Class Components

Hooks are a great new feature in React. The first initial case I found them useful was where we had to create class components just because we wanted to use a single ref or store one variable in state. Now in those cases, we can use hooks to write more succinct code. Here’s a quick example using useState :

Using useState

Old and Busted

class SearchBar extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      query: ''
    }
  }

  render() {
    return (
      <form action="https://duckduckgo.com">
        <input
          type="text"
          name="q"
          onChange={(e) => { this.setState({ query: e.target.value }) }}
        />
        <input type="submit" value="search" />
      </form>
    )
  }
}

New Hotness

const SearchBar = () => {
  const [query, setQuery] = useState('')
  return (
    <form action="https://duckduckgo.com" >
      <input
        type="text"
        name="q"
        onChange={(e) => { setQuery(e.target.value) }}
      />
      <input type="submit" value="search" />
    </form>
  )
}

Thinking in Effects

useRef is similar. However, working with useEffectis a bit different. It’s designed to replace several lifecycle methods, such as componentWillReceiveProps, componentWillMount, and componentWillUnmount. It doesn’t map one-to-one to old functionality like state and ref, which makes it more difficult to “think in effects.”

Here’s what finally made it click for me: the first two arguments of useEffect are a function (didUpdate), and an array of dependencies. the didUpdate function can return a function of its own, a cleanUp function. When a component mounts or the dependencies are updated, didUpdate is called. When the component unmounts, cleanUp is called if present.

Once you grok how effects work (“when this variable changes, do something”) over the lifecycle methods, you can save yourself a lot of headaches in code. You no longer have to write code like this:

Old and Busted: componentWillReceiveProps

  componentWillReceiveProps = (nextProps) => {
    if (nextProps.position !== this.props.position) {
      this.moveMap(nextProps.position)
    }
  }

You can now write more succicnt statements like this:

New Hotness: useEffect

useEffect(() => { moveMap(position) }, [position])

Here’s another example is using useEffect to replace componentDidMount and componentWillUnmount for setting and clearing event listeners. By declaring the dependencies array as empty, you only call the didUpdate and cleanUp functions once each. No dependencies mean no updates.

Old and Busted: componentWill(un)mount

  componentWillMount = () => {
    /* attach listeners to google StreetView */
    const streetView = this.getStreetView()
    window.google.maps.event.addListener(streetView, 'zoomChanged', this.handlePovChanged())
  }

  componentWillUnmount = () => {
    window.google.maps.event.clearInstanceListeners()
  }

  getStreetView = () => { /* ... */ }
  handlePovChanged = () => { /* ... */ }

New Hotness: useEffect

  const getStreetView = () => { /* ... */ }
  const handlePovChanged = () => { /* ... */ }
  const { addListener, clearInstanceListeners } = window.google.maps.event
  
  useEffect(() => {
    const streetView = getStreetView()
    addListener(streetView, 'on', handlePovChanged())
    return clearInstanceListeners
  }, []) // empty dependency array = only called on mount and unmount

Instead of grouping functionality by when they are in the lifecycle, you can group them by concern. It makes your code easier to reason about.

There’s one problem. You can’t use useEffect (or any other hook) in a class component. Hooks are only available in functional components. If you want to refactor your lifecycle methods to use useEffect, you have to refactor entire class components writ large. This is both time-consuming and prone to error. What if you could refactor just this one part of the code?

Use Hooks With Adapter Components

Considering that:

  • we cannot use hooks within class components…
  • we can use hooks within functional components…
  • And we can use functional components within class components…

We can see a solution: create a functional component that encapsulates the useEffect behavior, and use that in your class components! It’s a take on the adapter pattern from object-oriented programming: We create a wrapper that encapsulates the functionality of a piece of code (useEffect), while changing its interface. A hook in component’s clothing. 🐺

Here’s a simple one I use to replace componentWillReceiveProps checks like the one above.

Observer: useEffect Adapter Component

const observer = ({ value, didUpdate }) => {
  useEffect(() => {
    didUpdate(value)
  }, [value])
  return null // component does not render anything
}

Example Usage

<Observer value={this.props.position} didUpdate={this.moveMap} />

You don’t have to pass the argument to didUpdate, but in this case I found that the easiest way to have access to that variable. You could also create a similar component to handle mounting and unmounting behavior.

MountHandler: another useEffect Adapter

const mountHandler = ({ onMount, onUnMount }) => {
  useEffect(() => {
    onMount()
    return onUnMount
  },[])
  return null
}

You could combine these into one component, or create more specific adapters that encase more business logic themselves. Do what works best for your codebase. Ideally, these a stop-gap solution that help you refactor class-based components incrementally. Here’s the strategy I used:

  1. Identify a class-based component you want to refactor.
  2. Isolate part of the class-specific code.
  3. Create an adapter component that allows you to remove that class specific code (for example, lifecycle methods)
  4. Run tests, ensure that all functionality still works.
  5. Repeat until you have removed all class-specific code.
  6. Convert the component from a class to a function.

In the end, You end up with code thats easier to reason about and more performant. Hope this helps!

Test Clicks On Connected Components In Under 10 Lines of Code

Here’s a fairly benign component, that can give developers pause when its time to write unit tests for your components: 

const ClickableButton = props => (
  <button onClick={props.doSomething}>Click Me!</button>
)

const mapDispatchToProps () => ({
  doSomething: dispatch(someFancyAction())
})

export default connect(
  null, 
  mapDispatchToProps,
)(ClickableButton)

There is only thing worth testing here: That when we click the button, the function we passed in as a prop gets called. That prop function could be a complex chain of actions & API calls. You don’t want to stress about that, at least not in these tests.  But to do this, are we going to have to create mock store and a provider component just because it’s connected? Ugh!

There has to be a better way. 

You Can Remove The Redux Connection Entirely

While you want your component to be the default export, you can export the unconnected component, and just test that! Hat tip to Dave Ceddia for show me a better way to export a connected component.  All you have to do is change one line: 

export const ClickableButton = props => (

As an additional trick, if you have any helper functions not attached to the component, you can export those as well for easy testing. 

With Jest and Enzyme, The Rest is Easy

Inside our test, import the unconnected component. Then, you can create a mock function using Jest, and simulate the click using Enzyme. Here’s what the test looks like all together: 

describe('<ClickableButton />', () => {
  it('calls the doThing prop when the button is clicked', () => {
    const props = { doSomething: jest.fn() }
    const wrapper = shallow(<ClickableButton {...props} />)
    wrapper.find("button").first().simulate("click")
    expect(props.doSomething).toHaveBeenCalled()
  })
})

How to Perform HTTP Requests with React & Redux

Async is one of the toughest problems in front-end development. It’s one of the reasons Redux and React were created. React all started when Facebook had what seemed like a trivial problem: Sometimes, the “unread messages” count in the header and the footer of the page would be different. This Skinner box is so important to Facebook’s business model, they decided to build a whole framework around it. By managing all changes in a global state, and rendering components based off of that state exclusively, you eliminate these kinds of problems. 

Most Async requests come from talking back and forth with the server. Let’s look at how we can make updates to our Redux state with HTTP requests.

First, Installing the Middleware.

Redux doesn’t come with a way to handle this out of the box.  The typical model in Redux is that you call the dispatch function, passing in an action as an argument. The dispatch function gives that action to the reducer, which goes up to update the state. All of these are synchronous actions. What if we want to dispatch asynchronous actions? For that, we’ll be using middleware called “redux-thunk.” redux-thunk gives you the ability to dispatch functions or actions. These functions can then dispatch actions themselves, but more on that later. First, install the middleware:

import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'

const initial state = {
  posts_loading: false,
  posts: [],
  posts_error: null,
}

const configureStore = () => createStore(
  reducer,
  initialState,
  applyMiddleware(thunk)),
)

export default configureStore

 A Couple Quick Thunk Examples

A thunk is a special kind of action creator that returns a function. The function receives two arguments: dispatch and getState, which unsurprisingly is a function that returns the state. That’s another benefit of using thunk: it allows us to fire dispatches based on the current state, without passing in any arguments. Here are some examples:

const slowDispatch = () => {
  return (dispatch, getState) => {
     return setTimeout(() =>
       dispatch(increment()), 1000)
     )
  )
}

const maybeDispatch = () => {
  return (dispatch, getState) => {
    if (getState().allowIncrement) dispatch(increment())
  }
}

// If you want to be terser, you can write these as one-liners

const slowDispatch = () => (dispatch) => setTimeout(() => dispatch(increment()),1000)

const maybeDispatch = () => (dispatch, getState) => getState().allowIncrement ? dispatch(increment()) : null

Create the actions & action creators.

aka good old Redux boilerplate. We’ll be following the ducks pattern for this exercise.

First, there are three actions, one that will tell us when the request started, one for handling success, and one for handling errors. We’ll create types for these. You could just use the strings instead of declaring constants, but this strategy helps you more quickly identify typos when debugging. A mistyped action will throw an error instead of failing silently.

// types.js
const LOAD_POSTS_REQUEST = 'tasks/history/request'
const LOAD_POSTS_SUCCESS = 'tasks/history/success'
const LOAD_POSTS_FAILURE = 'tasks/history/failure'

export default { 
  LOAD_POSTS_REQUEST,
  LOAD_POSTS_SUCCESS,
  LOAD_POSTS_FAILURE,
}

Then, we’ll create our action creators. Again, we could do this inside of our thunk, but this helps keep the code clean. You can also use these when writing tests, and they will help you write them more efficiently.  We’ll create three: one to update the state when we are loading, one when we get a successful response, and one when we get an error.

import types from './types'

const loadPostsRequest = () => ({ type: types.LOAD_POSTS_REQUEST })

const loadPostsSuccess = posts => ({
  type: types.LOAD_POSTS_SUCCESS,
  payload: posts,
})

const loadPostsFailure = error => ({
  type: types.LOAD_POSTS_FAILURE,
  payload: error,
  error: true,
})

export default {
  loadPostsRequest,
  loadPostsSuccess,
  loadPostsFailure,
}

Updating Our Reducer

Now that we have actions, we need to handle them in our reducer. We’ll store three variables. First, a boolean to track the loading state. We can use this to toggle loading indicators in our interface. We’ll also store the results in an array, and store the error we get in response if there is a problem.

port types from './types'

const postReducer = (state = {}, action) => {
  switch (action.type) {
    case types.LOAD_POST_REQUEST:
      return {
        ...state,
        posts_loading: true,
      }
      case types.LOAD_POST_SUCCESS:
        return {
          ...state,
          posts_loading: false,
          posts: action.payload,
        }
        case types.LOAD_POST_FAILURE:
        return {
          ...state,
          posts_loading: false,
          posts_error: action.payload,
        }
        //...other actions
        default:
           return state 
        }
    }
}

Who Will Dispatch the Dispatches? Creating Our Thunk

In the ducks pattern, operations are higher-order action creators. Thunks are one category of operation. Another way to think of an operation is an action creator+. It will dispatch actions, sometimes more than one, and sometimes take care of other business logic. Again what makes thunks specific is that return a function instead of an action. 

In our operations, we’ll be deploying some combination of the three actions we defined earlier. We’ll be using Axios to make our HTTP request.

const fetchPosts = () => {
  const url = '/our-app/posts.json'
  return (dispatch) => {
    dispatch(actions.loadPostsRequest())
    axios.get(url)
      .then((response) => {
        dispatch(actions.loadPostsSuccess(response.data))
      })
      .catch((error) => {
        dispatch(actions.loadTaskHistoryFailure(error))
      })
  }
}

And there you have it! Now that you’ve written your thunk, you want to make sure it’s well tested and resilient to change. In the next article, you’ll learn exactly how to test redux thunks.