Jak wiemy, zasada brzmi:

Tylko haczyki na najwyższym poziomie. Nie nazywaj haczykami wewnątrz pętli, warunków ani zagnieżdżonych funkcji.

Więc moje pytania są jak korzystać i zaprojektować niestandardowy haczyk, który jest drogi?

Biorąc pod uwagę ten haczyk:

const useExpensiveHook = () => {
    // some code that uses other built-in hooks...
    const value = computeExpensiveValue();
    // some more code....
    return value;
}

Jeśli ta zasada nie istniała, mój kod klienta byłby:

const myComponent = ({isSuperFeatureEnabled}) => {
   let value;
   if (isSuperFeatureEnabled){
      value = useExpensiveHook();
   }

   return <div>{value}</div>;
}

Rozwiązaniem, z którym się wymyśliłem, jest pozwolenie, aby haczyk wiedział, że powinien oszukać, jak ten, używając flagi:

const useExpensiveHook = ({enabled}) => {
    // some code that uses other built-in hooks...
    let value;
      if(enabled) {
          value = computeExpensiveValue();
      }
      else {
          value = undefined;
      }
    // some more code....
    return value;
};

I kod klienta:

const myComponent = ({isSuperFeatureEnabled}) => {
   const value = useExpensiveHook({enabled : isSuperFeatureEnabled});
   return <div>{value}</div>;
}

Przekazuje flagę do drogich haków właściwy sposób obsługi haczyków warunkowych? Jakie są inne opcje?

2
Sylvain 21 luty 2019, 17:37

2 odpowiedzi

Najlepsza odpowiedź

W oryginalnym przykładzie jest to wartość początkowa, która jest droga, a nie sam hak, computeExpensiveValue może być warunkowo nazywany:

const [value, setValue] = useState(enabled ? computeExpensiveValue() : undefined);

W aktualnie wymienionych przykład useExpensiveHook nie jest haczykiem, ale niektóre funkcje; Nie używa reakcji haków.

Celem cytowanej zasady jest stworzenie wbudowanych haków zwanych bezwarunkowo, ponieważ stan haczyków jest określony przez kolejność, w której nazywają się:

if (flipCoin())
  var [foo] = useState('foo');

var [bar] = useState('bar');

W przypadku useState('foo') nie jest wywoływanym na następnym renderowaniu komponent, useState('bar') staje się pierwszym hakiem useState, który ma być nazywany i rozważany foo, podczas gdy druga useState jest Brakujące, ta niespójność wyzwala błąd w rendererze.

Gdyby zagwarantowało, że zamówienie połączeń hooków jest zachowywany, byłoby to akceptowalne w stosowaniu warunków, ale rzadko jest to możliwe w praktyce. Nawet jeśli jest pozornie stały stan jak if (process.env.NODE_ENV === 'development'), może się zmienić w pewnych okolicznościach w czasie wykonywania i skutkują wspomniane problemy, które trudno debugować.

Poprawny:

useEffect(() => {
  if (varyingCondition)
    computeExpensiveValue();
});

Błędny:

if (varyingCondition)
  useEffect(() => {
    computeExpensiveValue();
  });

Zasada ta ma zastosowanie tylko do wbudowanych haków i funkcji, które nazywają je bezpośrednio lub pośrednio (tak zwane niestandardowe haczyki). Tak długo, jak computeExpensiveValue nie używa wbudowanych haków wewnętrznie, może być warunkowo nazywany, jak pokazano "poprawne" przykłady.

W przypadku, gdy składnik musi warunkowo stosować haczyk osób trzecich w zależności od flagi, należy zagwarantować, że warunek nie zmienia się z czasem, ograniczając go do wstępnej wartości propy:

const Component = ({ expensive, optionalValue }) => {
  const isExpensive = useMemo(() => expensive, []);
  if (isExpensive)
    optionalValue = useExpensiveHook();
  return ...
}

W ten sposób <Component expensive={flipCoin()} /> nie złamie reguły, ale po prostu nadużywa komponent.

Ponieważ należy je znać, jeśli potrzebny jest drogi hak w czasie, gdy używany jest <Component expensive/>, czyszczącą drogą jest komponowanie tej funkcji w składniku wyższego rzędu i używać różnych składników w zależności od tego, który jest potrzebny:

const withExpensive = Comp => props => {
  const optionalValue = useExpensiveHook();
  return <Comp optionalValue={optionalValue} ...props />;
}

const Component = ({ optionalValue }) => {
  return ...
}

const ExpensiveComponent = withExpensive(Component);
0
Estus Flask 22 luty 2019, 14:15

Argument do useState jest używany tylko raz, a tym samym, jeśli początkowo przejdziesz enabled jako fałszywe do niego, nie będzie on wykonać computeExpensiveValue nigdy. Dlatego też musisz dodać połączenie useEffect. Zamiast tego możesz zaprojektować swój hak

const useExpensiveHook = ({enabled}) => {
    const [value, setValue] = useState(enabled ? computeExpensiveValue : undefined);

    useEffect(()=> {
      if(enabled) {
          const value = computeExpensiveValue();
          setValue(value);
      }
    }, [enabled]);

    // some more code.... 
    return value;
};
0
Shubham Khatri 21 luty 2019, 15:02