1
votes

Utilisation de certains accessoires pour déclencher un hook useEffect () React, mais en référençant d'autres dans l'effet

J'ai un cas d'utilisation de React où un composant obtient une prop category transmise, et a également une valeur d'état local limit . Mon comportement souhaité est:

  1. Lorsqu'une catégorie est sélectionnée, la limite est réinitialisée à 10 si elle est actuellement supérieure à 10.

  2. Lorsque la catégorie est effacée, la limite est réinitialisée à 50 si elle est actuellement inférieure à 50.

  3. Tant que la catégorie est inchangée, l'utilisateur peut choisir la limite de son choix.

J'ai ce comportement défini dans un hook d'effet comme celui-ci (exemple plus complet en bas):

function App() {
  const [category, setCategory] = useState(true);
  return (
    <div className="App">
      <button onClick={() => setCategory(!category)}>Toggle category</button>
      <List category={category} />
    </div>
  );
}

function List({ category }) {
  const [limit, setLimit] = useState(10);

  // Change the limit when category changes
  // BUT only if limit is above/below a threshold!
  useEffect(() => {
    if (category && limit > 10) {
      setLimit(10);
    } else if (!category && limit < 50) {
      setLimit(50);
    }
  }, [category]);

  return (
    <div>
      <select value={limit} onChange={e => setLimit(e.target.value)}>
        {[5, 10, 25, 50, 100].map(d => (
          <option key={d}>{d}</option>
        ))}
      </select>
      <div>Category is {category ? "on" : "off"}</div>
      <div>Would show {limit} items</div>
    </div>
  );
}

Cela fonctionne bien, mais va à l'encontre du react-hooks / exhaust-deps linter rule qui dit que je devrais inclure limit dans le tableau de dépendances. Je ne veux pas vraiment cela, car je ne veux pas qu'une modification de la limite déclenchée par l'utilisateur déclenche quoi que ce soit, et je veux qu'un utilisateur puisse dépasser les seuils haut / bas pendant que le la catégorie ne change pas.

Y a-t-il une "bonne" façon d'accomplir cela qui respecte les règles des hooks tout en n'introduisant pas plusieurs hooks supplémentaires afin de jongler avec toutes les différentes transitions d'accessoires / états possibles? / p>

Exemple plus complet:

  useEffect(() => {
    if (category && limit > 10) {
      setLimit(10);
    } else if (!category && limit < 50) {
      setLimit(50);
    }
  }, [category]);


0 commentaires

3 Réponses :



0
votes

Passez à useReducer () pour gérer la limite avec la catégorie et transmettre les deux à votre composant au lieu d'utiliser l'état local. De cette façon, le composant sera rendu uniquement lorsque l'une des valeurs change (ou les deux).

const initialState = {
    category: true,
    limit: 10
};

function reducer(state, action) {
    switch (action.type) {
    case 'toggleCategory':
        if (state.category) {
            return {category: false, limit: Math.min(50, state.limit)};
        }
        else {
            return {category: true, limit: Math.max(10, state.limit)};
        }
    case 'setLimit':
        return {...state, limit: action.limit};
    default:
        throw new Error();
    }
}

function App() {
    const [state, dispatch] = useReducer(reducer, initialState);
    // React guarantees that dispatch function identity is stable and won’t change on re-renders.
    // This is why it’s safe to omit from the useEffect or useCallback dependency list.
    const toggleCategory = useCallback(() => dispatch({type: 'toggleCategory'}));
    const setLimit = useCallback(limit => dispatch({type: 'setLimit', limit}));
    return (
        <div className="App">
            <button onClick={toggleCategory}>Toggle category</button>
            <List {...state} setLimit={setLimit} />
        </div>
    );
}

function List({ category, limit, setLimit }) {
    return (
        <div>
            <select value={limit} onChange={e => setLimit(e.target.value)}>
                {[5, 10, 25, 50, 100].map(d => (
                    <option key={d}>{d}</option>
                ))}
            </select>
            <div>Category is {category ? "on" : "off"}</div>
            <div>Would show {limit} items</div>
        </div>
    );
}


0 commentaires

0
votes

Vous pouvez utiliser la version fonctionnelle de setter:

useEffect(() => {
    setLimit((currentLimit) => {
        if (category && currentLimit > 10) {
          return 10;
        } else if (!category && currentLimit < 50) {
          return 50;
        } else return currentLimit;
    });
  }, [category]);


0 commentaires