Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

111
Views
confusion about this React custom hook usage

I was looking at some tutorial on React Hooks and in the tutorial the author created a useDropdown hook for rendering reusable dropdowns. The code is like this

import React, { useState } from "react";

const useDropdown = (label, defaultState, options) => {
  const [state, updateState] = useState(defaultState);
  const id = `use-dropdown-${label.replace(" ", "").toLowerCase()}`;
  const Dropdown = () => (
    <label htmlFor={id}>
      {label}
      <select
        id={id}
        value={state}
        onChange={e => updateState(e.target.value)}
        onBlur={e => updateState(e.target.value)}
        disabled={!options.length}
      >
        <option />
        {options.map(item => (
          <option key={item} value={item}>
            {item}
          </option>
        ))}
      </select>
    </label>
  );
  return [state, Dropdown, updateState];
};

export default useDropdown;

and he used this in a component like this

import React, { useState, useEffect } from "react";
import useDropdown from "./useDropdown";

const SomeComponent = () => {
  const [animal, AnimalDropdown] = useDropdown("Animal", "dog", ANIMALS);
  const [breed, BreedDropdown, updateBreed] = useDropdown("Breed", "", breeds);

  return (
    <div className="search-params">
      <form>
        <label htmlFor="location">
          Location
          <input
            id="location"
            value={location}
            placeholder="Location"
            onChange={e => updateLocation(e.target.value)}
          />
        </label>
        <AnimalDropdown />
        <BreedDropdown />
        <button>Submit</button>
      </form>
    </div>
  );
};

export default SomeComponent;

He said this way we can create reusable dropdown components. I was wondering how is this different from defining a plain old Dropdown component and pass props into it. The only difference I can think of in this case is that now we have the ability to get the state and setState in the parent component(i.e. SomeComponent) and read / set the state of the child(i.e. the component output by useDropdown) directly from there. However is this considered an anti-pattern since we are breaking the one way data flow?

over 4 years ago · Santiago Trujillo
2 answers
Answer question

0

While there is no hard core restriction on how you should define custom hooks and what logic should the contain, its an anti-pattern to write hooks that return JSX

You should evaluate what benefits each approach gives you and then decide on a particular piece of code

There are a few downsides to using hooks to return JSX

  • When you write a hook that returns JSX component, you are essentially defining the component within the functional component, so on each and every re-render you will be creating a new instance of the component. This will lead to the component being unmounted and mounted again. Which is bad for performance and also buggy if you have stateful login within the component as the state will get reset with every re-render of parent
  • By defining a JSX component within the hook, you are taking away the option of lazy loading your component if the need be.
  • Any performance optimization to the component will require you to make use of useMemo which doesn't give you the flexibility of a custom comparator function like React.memo

The benefit on the other hand is that you have control over the state of the component in the parent. However you can still implement the same logic by using a controlled component approach

import React, { useState } from "react";

const Dropdown = Reat.memo((props) => {
  const { label, value, updateState, options } = props;
  const id = `use-dropdown-${label.replace(" ", "").toLowerCase()}`;
  return (
    <label htmlFor={id}>
      {label}
      <select
        id={id}
        value={value}
        onChange={e => updateState(e.target.value)}
        onBlur={e => updateState(e.target.value)}
        disabled={!options.length}
      >
        <option />
        {options.map(item => (
          <option key={item} value={item}>
            {item}
          </option>
        ))}
      </select>
    </label>
  );
});

export default Dropdown;

and use it as

import React, { useState, useEffect } from "react";
import useDropdown from "./useDropdown";

const SomeComponent = () => {
  const [animal, updateAnimal] = useState("dog");
  const [breed, updateBreed] = useState("");

  return (
    <div className="search-params">
      <form>
        <label htmlFor="location">
          Location
          <input
            id="location"
            value={location}
            placeholder="Location"
            onChange={e => updateLocation(e.target.value)}
          />
        </label>
        <Dropdown label="animal" value={animal} updateState={updateAnimal} options={ANIMALS}/>
        <Dropdown label="breed" value={breed} updateState={updateBreed} options={breeds}/>
        <button>Submit</button>
      </form>
    </div>
  );
};

export default SomeComponent;
over 4 years ago · Santiago Trujillo Report

0

Anti-pattern is such a blunt phrase to describe simple of otherwise complex solutions that other developers don't agree with. I agree with Drew's point of view that the hook breaks conventional design, by doing more than it should.

As per React's hook documentation, the purpose of a hook is to allow you to use state and other React features without writing a class. This is typically considered to be setting state, performing computational tasks, doing API or other queries in an asynchronous matter, and responding to user input. Ideally, a functional component should be interchangeable with a class component, but in reality, this is far more difficult to achieve.

The particular solution for creating Dropdown components, while it works, isn't a good solution. Why? It's confusing, it isn't self explanatory and it's difficult to comprehend what's happening. With hooks, they should be simple and perform a single task, eg a button callback handler, calculating and returning a memoized result, or doing some other task you would normally delegate to this.doSomething().

Hooks that return JSX aren't really hooks at all, they're just Functional Components, even if they use the correct prefix naming convention for hooks.

There's also confusion around React and one-way communication for component updates. There's no restriction on which way data can pass, and can be treated in a similar fashion to Angular. There are libraries such as mobx which allows you to subscribe and publish changes to shared class properties, which will update any UI component that listens, and that component can update it too. You can also use RxJS to make asynchronous changes at any time, that can update the UI.

The specific example does steer away from SOLID principles, providing input points for the parent component to control the data of the child component. This is typical of strongly typed languages, such as Java, where it's more difficult to do asynchronous communication (not really a problem these days, but it used to be). There's no reason why a parent component should not be able to update a child component - it's a fundamental part of React. The more abstraction you add, the more complexity your add, and more points of failure.

Adding the use of asynchronous functions, observables (mobx/rxjs), or context can reduce the direct data coupling, but it will create a more complex solution.

over 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!