简体   繁体   English

使用 React 道具作为 function 参数

[英]Use React props as a function argument

I want to check if this is good practice, and if there's a better approach to it.我想检查这是否是好的做法,以及是否有更好的方法。 I have a function that is called when the component loads and when clicking on a couple of buttons, so I need to use it inside useEffect, and pass it to other components.我有一个 function 在组件加载单击几个按钮时被调用,所以我需要在 useEffect 中使用它,并将其传递给其他组件。 I could use useCallback, but I don't see how this approach is not enough, since the components that can call getWeatherHere also need isCelsius, thus being able to use it as argument (the argument and the state are the same).我可以使用useCallback,但我不明白这种方法是如何不够的,因为可以调用getWeatherHere的组件也需要isCelsius,因此可以将其用作参数(参数和state相同)。

export default function weatherInfo() {
  const [weatherInfo, setWeatherInfo]: [IWeather, React.Dispatch<IWeather>] = useState();
  const [isCelsius, setIsCelsius] = useState(true);

  function getWeatherHere(isCelsius: boolean) {
    navigator.geolocation.getCurrentPosition(async ({coords: {latitude, longitude}}) => {
      const data = await Weather.getWeather(latitude, longitude, !isCelsius ? 'imperial' : undefined);
      setWeatherInfo(data);
    });
  }

  useEffect(() => {
    getWeatherHere(isCelsius);
  }, [isCelsius]);

  return (
    <OtherComponent isCelsius={isCelsius} getWeatherHere={getWeatherHere}/>
  );
}

This is how you do it, but there are tools available to make this easier.这就是你的做法,但有一些工具可以让这更容易。

Consider using a construction like useAsync from react-use .考虑使用react-use中的useAsync类的结构。 It allows you to express a Promise as a state.它允许您将 Promise 表达为 state。 This avoids all sort of pitfalls and issues of using Promises in functional Components.这避免了在功能组件中使用 Promise 的各种陷阱和问题。

So you get something like:所以你会得到类似的东西:

const state = useAsync(async () => {
    const { coords: { latitude, longitude } } = await navigator.geolocation.getCurrentPosition();
    return {
        isCelsius: isCelsius,
        data: await Weather.getWeather(latitude, longitude, !isCelsius ? 'imperial' : undefined),
    };
}, [ isCelsius ]);

return <>
    {state.loading === false && <OtherComponent 
        isCelsius={state.value.isCelsius}
        weatherHere={state.value.data}
    />
</>;

Some remarks:一些备注:

  1. I added isCelsius to the value of the async state.我将isCelsius添加到异步 state 的值中。 If you don't do that, and pass isCelsius directly, you're going to have a desynch between the value of isCelsius and the weather data.如果你不这样做,直接传递isCelsius ,你将在isCelsius的值和天气数据之间产生不同步。 I would actually expect that the temperature unit is part of the result of the getWeather request, but if it's not, this works.我实际上希望温度单位是 getWeather 请求结果的一部分,但如果不是,这可行。

  2. There is one problem when using promises with setState in React, and that has to do with cleanups.在 React 中使用带有 setState 的 Promise 时存在一个问题,这与清理有关。 If your Component is unmounted before the promise is completed, it doesn't magically cancel the code.如果您的组件在 promise 完成之前卸载,它不会神奇地取消代码。 It will finish the request and call setWeatherInfo .它将完成请求并调用setWeatherInfo That will trigger a re-render on an unmounted component, which React will give you a warning about.这将触发一个未安装组件的重新渲染,React 会给你一个警告。 It's not a huge problem in this case, but it can become a problem in more complex situations.在这种情况下这不是一个大问题,但在更复杂的情况下它可能会成为一个问题。 useAsync takes care of this by checking if the component is still mounted at the end of the fetcher function. useAsync通过检查组件是否仍然安装在 fetcher function 的末尾来处理这个问题。 You can also do that yourself by using usePromise , but I would try to avoid using Promises in this way all together.你也可以自己使用usePromise ,但我会尽量避免以这种方式一起使用 Promises。

  3. Your code can suffer from race conditions.您的代码可能会受到竞争条件的影响。 If you change isCelsius quickly a couple of times, it's going to be a coinflip which result is going to end up being used.如果您快速更改isCelsius几次,这将是一个硬币翻转,其结果将最终被使用。 useAsync also takes care of this. useAsync也负责这一点。

If, instead of passing the weather, you want to pass a function that fetches the weather, use useAsyncFn instead.如果您想要传递获取天气的 function 而不是传递天气,请改用useAsyncFn The state is the same, but it also returns a function that allows you to call the fetcher function. state 是一样的,但它也返回一个 function 允许您调用 fetcher function。 This is in addition to the value of isCelsius changing.这是对isCelsius值变化的补充。

As your post is about best practices, I'll let you my 2 cents.由于您的帖子是关于最佳实践的,我会给您 2 美分。 Some things that I would change using only pure react to refactor it.有些事情我会改变,只使用纯反应来重构它。

export default function weatherInfo() {
  // You don't need to type both returns, only the hook.
  const [weatherInfo, setWeatherInfo] = useState<IWeather | undefined>(undefined);

  // Why do you need this state if it's not changing? 
  // May you can put this in a const and save one state.
  const isCelsius = true;

  // I may change this "Here" in the function, because it don't look wrong, by it's subjective
  // Use "ByUserLocation" it's a better description
  // With the hook useCallback you can rerender the function only based on the right values
  const getWeatherByUserLocation = useCallback(() => {
    // Extracting the function improves the maintenance by splitting each step of the process and each callback function.
    const callbackFn = async ({coords: {latitude, longitude}}) => {
      // Turn your validations into variables with a well-descriptive name.
      // Avoid making validation starting by negating the value;
      const temperatureUnit = isCelsius ? 'celsius' : 'imperial'; 

      // Avoid using "data" as the name of a variable even when it looks clear what the data is. In the future, you will not remember if this logic should change.
      const weatherInfoData = await Weather.getWeather(
        latitude, 
        longitude, 
        temperatureUnit
      );

      setWeatherInfo(weatherInfoData);
    };

    navigator.geolocation.getCurrentPosition(callbackFn);
  }, [isCelsius]);

  useEffect(() => {
    getWeatherByUserLocation();
    // removing the prop from the function, you avoid calling it many times based on the state change
    // A useEffect without dependencies on the dependency array, it will only be called on the component mount and unmount
  }, []);

  return (
    <OtherComponent isCelsius={isCelsius} getWeatherByUserLocation={getWeatherByUserLocation}/>
  );
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM