What are hooks?

React 16.8 released in 2019 introduced hooks and since then the trend has been to move away from stateful class-based components and instead use the simpler stateless-first functional patterns, which you can still sprinkle with hooks such as useState() to make them stateful.

Flutter has them too?

In the world of Flutter, a similar pattern has emerged in the form of the flutter_hooks package, which lets you avoid Flutter’s overly complex and ugly-looking stateful widget syntax, while still have access to local state, via syntax like useState() - i.e. very familiar-looking to a React developer.

Btw, if you’re just diving in to flutter_hooks, apart from the obvious useState() make sure to also check out hooks like useFuture(), useMemoized() and useIsMounted(), which all come in handy if you start doing async calls that affect the widget state.

useState() hell

Things can quickly get out of hand though, as Steve of builder.io describes

import { useState } from 'react'

function EditCalendarEvent() {
  const [startDate, setStartDate] = useState()
  const [endDate, setEndDate] = useState()
  const [title, setTitle] = useState('')
  const [description, setDescription] = useState('')
  const [location, setLocation] = useState()
  const [attendees, setAttendees] = useState([])
  
  return <>
    <input value={title} onChange={e => setTitle(e.target.value)} />
    {/* ... */}
  </>
}

He makes the situation more managable (while crediting David Khourshid for the idea), by combining the semi related state flags into a single object managed by a reducer, and also utilizing the reducer’s update function as a pre-commit decline / adjustment hook of sorts - a central place to validate and modify input before an update is allowed to happen:

  const [event, updateEvent] = useReducer(
    (prev, next) => {
      const newEvent = { ...prev, ...next };

      // Ensure that the start date is never after the end date
      if (newEvent.startDate > newEvent.endDate) {
        newEvent.endDate = newEvent.startDate;
      }

      // Ensure that the title is never more than 100 chars
      if (newEvent.title.length > 100) {
        newEvent.title = newEvent.title.substring(0, 100);
      }
      return newEvent;
    },
    { title: "", description: "", attendees: [] }
  );

That’s neat!

Check out the video in Steve’s tweet, where he introduces the concept better than this post ever could.

Can we do it in Flutter too?

Yes!

Fortunately, useReducer is implemented by flutter_hooks too, so the same idea should translate just fine!

Reduce-what?

useReducer() is related to Array.prototype.reduce() in JavaScript and might seem odd and backwards if you haven’t been schooled in map/reduce patterns before.

You can certainly write cryptic code with a reducer involved, it can be an excellent foot-gun in that regard. But it is also concise and reliable once you get it right, fewer long-lived temp vars spinkled all over your code, mostly a unidirectional stream of data, with the tricky logic consolidated in one place.

Steve’s point is probably the opposite of making things more complex, and more about the observation that useReducer() happens to provide both a way to store state and a pre-write callback perfect to hold validation code - which trumps the temp var hell that overuse of loosely releated useState() calls can lead to. If you insist on keeping the reduce function readable instead of clever, you will for sure have a net benefit of picking useReducer() over useState() in many situations.




sponsor

Need private photo sharing that's not tied up in the social media circus? Photoroll is a good way to share photos with your clients.
Or privately gather media from participants of weddings and other small to medium-sized events.