如何确保组件内的常量仅在使用钩子的 React(本机)启动时计算一次?

Joe*_*eBe 5 reactjs react-native react-state-management react-native-svg react-hooks

我目前正在创建一个 React Native 应用程序,但仍然不确定如何最好地处理我的用例的状态。

我用来react-native-svg在应用程序中创建一个相当复杂的图形,它存储在一个组件中,保持不变并在父组件中检索。然后这个父组件在画布上有规律地旋转和平移,但复杂图形的路径不会改变。因此,由于计算量相当大,我只想在应用程序开始时计算该图形的 SVG 路径,而不是在我旋转或平移父组件时计算。目前,我的代码结构如下所示:

图形组件

const Figure = (props) => {
    const [path, setPath] = useState({FUNCTION TO CALCULATE THE SVG PATH});

    return(
        <G>
            <Path d={path} />
        </G>
    )
}
Run Code Online (Sandbox Code Playgroud)

父组件

const Parent = (props) => {
    return (
        <View>
            <Svg>
                <G transform={ROTATE AND TRANSFORM BASED ON INTERACTION}>
                    <Figure />
                </G>
            </Svg>
        </View>
    )
}
Run Code Online (Sandbox Code Playgroud)

如您所见,我使用了路径数据的状态。我现在的问题:

  1. 我是否在这里确保此路径仅在开始时计算一次?
  2. 有没有更优雅/更好的方法来做到这一点(例如,我现在根本不使用 setter 函数,所以我觉得这不是最佳做法)

编辑:

计算路径的函数取决于我从父组件传递的一些道具。

JMa*_*ine 8

当您将值传递给该值时,useState该值用于初始化状态。它不会在每次组件重新渲染时设置,因此在您的情况下,path只有在组件安装时才会设置。

就算道具变了,path状态也不会。

由于您的初始状态取决于道具,因此您需要做的就是将相关道具从道具中拉出并将其传递给计算初始path值的函数:

const Figure = (props) => {
  // get relevant prop
  const { importantProp } = props;

  // path will never change, even when props change
  const [path] = useState(calculatePath(importantProp));

  return(
    <G>
      <Path d={path} />
    </G>
  )
}
Run Code Online (Sandbox Code Playgroud)

但是,calculatePath即使该path值未重新初始化,该函数仍会在每次渲染时进行评估。如果calculatePath是一个昂贵的操作,那么考虑使用useMemo

您可以path通过将该道具添加到依赖项数组来确保仅在特定道具更改时更新:

const Figure = (props) => {
  const { importantProp } = props;

  const path = useMemo(() => calculatePath(importantProp), [importantProp]);

  return(
    <G>
      <Path d={path} />
    </G>
  )
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,您根本不需要状态。

添加importantPropuseMemo依赖数组意味着每次importantProp更改时,React 都会重新计算您的path变量。

使用useMemo将防止在每次渲染时评估昂贵的计算。

我创建了一个CodeSandbox 示例,您可以在控制台中看到只有在指定的 propimportant更改时才会重新计算路径。如果您更改任何其他道具,则path不会重新计算。

  • `useEffect` 用于在组件渲染或某些依赖项发生变化时调用函数。这是您应该执行副作用的地方。`useMemo` 用于防止在每次渲染时重新创建昂贵的对象,因为只有当依赖项发生变化时才会重新创建它。对于您的情况,两者都可以工作,但是“useMemo”更合适,因为您试图保留一个对象而不是产生副作用。副作用可以是任何东西,包括调用设置状态。 (2认同)

Cer*_*nce 6

一个更优雅的选择是使用 的回调形式useState。如果您向其传递一个函数,则仅当需要计算初始状态时(即初始渲染时)才会调用该函数:

const [path, setPath] = React.useState(heavyCalculation);
Run Code Online (Sandbox Code Playgroud)

const [path, setPath] = React.useState(heavyCalculation);
Run Code Online (Sandbox Code Playgroud)
const heavyCalculation = () => {
  console.log('doing heavy calculation');
  return 0;
};

const App = () => {
  const [path, setPath] = React.useState(heavyCalculation);
  React.useEffect(() => {
    setInterval(() => {
      setPath(path => path + 1);
    }, 1000);
  }, []);
  return 'Path is: ' + path;
};
ReactDOM.render(<App />, document.querySelector('.react'));
Run Code Online (Sandbox Code Playgroud)

正如您在上面的代码片段中看到的,doing heavy calculation只记录一次。

  • 这是最好的答案。https://reactjs.org/docs/hooks-reference.html#lazy-initial-state (3认同)