如何确保组件内的常量仅在 React (Native) 中使用 hooks 启动时计算一次?
How to ensure a constant inside a component is only calculated once on start in React (Native) with hooks?
我目前正在创建一个 React Native 应用程序,但仍然不确定如何最好地处理我的用例的状态。
我使用 react-native-svg
在应用程序中创建了一个相当复杂的图形,它存储在组件中,保持不变并在父组件中检索。然后这个父组件在canvas上有规律的旋转和平移,但是复图的路径没有变化。因此,由于计算量很大,我只想在应用程序启动时计算该图形的 SVG 路径,而不是在旋转或平移父组件时计算。目前,我的代码结构如下所示:
图形组件
const Figure = (props) => {
const [path, setPath] = useState({FUNCTION TO CALCULATE THE SVG PATH});
return(
<G>
<Path d={path} />
</G>
)
}
父组件
const Parent = (props) => {
return (
<View>
<Svg>
<G transform={ROTATE AND TRANSFORM BASED ON INTERACTION}>
<Figure />
</G>
</Svg>
</View>
)
}
如您所见,我正在为路径数据使用状态。我现在的问题:
- 我在这里确保这条路径只在开始时计算一次吗?
- 是否有更多 elegant/better 的方法(例如,我现在根本没有使用 setter 函数,所以我觉得这不是最佳做法)
编辑:
计算路径的函数取决于我从父组件传递的一些道具。
你可以使用钩子useMemo,它returns你的const的值,它接收returns这个值的函数,也是一个数组,发送一个空的只计算一次,如果需要,你可以发送一些依赖项,道具或状态,以便在它们发生变化时重新计算值。
在你的情况下,你可以这样做:
const Figure = (props) => {
const path = React.useMemo(() => {
// calculate and return value for path
}, []);
return(
<G>
<Path d={path} />
</G>
)
}
它是为那只海豚创建的。
希望对您有所帮助 ;)!
当您将值传递给 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>
)
}
但是,即使 path
值未重新初始化,calculatePath
函数仍会在每次渲染时进行评估。如果 calculatePath
是一个昂贵的操作,那么考虑使用 useMemo
代替:
您可以确保 path
仅在特定道具更改时更新,方法是将该道具添加到依赖项数组:
const Figure = (props) => {
const { importantProp } = props;
const path = useMemo(() => calculatePath(importantProp), [importantProp]);
return(
<G>
<Path d={path} />
</G>
)
}
在那种情况下,您根本不需要状态。
将 importantProp
添加到 useMemo
依赖项数组意味着每次 importantProp
更改时,React 都会重新计算您的 path
变量。
使用 useMemo
将避免在每次渲染时进行昂贵的计算。
我创建了一个 CodeSandbox example,您可以在控制台中看到路径仅在指定的 prop important
更改时才重新计算。如果您更改任何其他道具,则 path
不会重新计算。
一个更优雅的选择是使用 useState
的回调形式。如果你给它传递一个函数,这个函数只会在需要计算初始状态时被调用——也就是说,在初始渲染时:
const [path, setPath] = React.useState(heavyCalculation);
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'));
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div class="react"></div>
如您在上面的代码片段中所见,doing heavy calculation
仅记录一次。
我目前正在创建一个 React Native 应用程序,但仍然不确定如何最好地处理我的用例的状态。
我使用 react-native-svg
在应用程序中创建了一个相当复杂的图形,它存储在组件中,保持不变并在父组件中检索。然后这个父组件在canvas上有规律的旋转和平移,但是复图的路径没有变化。因此,由于计算量很大,我只想在应用程序启动时计算该图形的 SVG 路径,而不是在旋转或平移父组件时计算。目前,我的代码结构如下所示:
图形组件
const Figure = (props) => {
const [path, setPath] = useState({FUNCTION TO CALCULATE THE SVG PATH});
return(
<G>
<Path d={path} />
</G>
)
}
父组件
const Parent = (props) => {
return (
<View>
<Svg>
<G transform={ROTATE AND TRANSFORM BASED ON INTERACTION}>
<Figure />
</G>
</Svg>
</View>
)
}
如您所见,我正在为路径数据使用状态。我现在的问题:
- 我在这里确保这条路径只在开始时计算一次吗?
- 是否有更多 elegant/better 的方法(例如,我现在根本没有使用 setter 函数,所以我觉得这不是最佳做法)
编辑:
计算路径的函数取决于我从父组件传递的一些道具。
你可以使用钩子useMemo,它returns你的const的值,它接收returns这个值的函数,也是一个数组,发送一个空的只计算一次,如果需要,你可以发送一些依赖项,道具或状态,以便在它们发生变化时重新计算值。
在你的情况下,你可以这样做:
const Figure = (props) => {
const path = React.useMemo(() => {
// calculate and return value for path
}, []);
return(
<G>
<Path d={path} />
</G>
)
}
它是为那只海豚创建的。
希望对您有所帮助 ;)!
当您将值传递给 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>
)
}
但是,即使 path
值未重新初始化,calculatePath
函数仍会在每次渲染时进行评估。如果 calculatePath
是一个昂贵的操作,那么考虑使用 useMemo
代替:
您可以确保 path
仅在特定道具更改时更新,方法是将该道具添加到依赖项数组:
const Figure = (props) => {
const { importantProp } = props;
const path = useMemo(() => calculatePath(importantProp), [importantProp]);
return(
<G>
<Path d={path} />
</G>
)
}
在那种情况下,您根本不需要状态。
将 importantProp
添加到 useMemo
依赖项数组意味着每次 importantProp
更改时,React 都会重新计算您的 path
变量。
使用 useMemo
将避免在每次渲染时进行昂贵的计算。
我创建了一个 CodeSandbox example,您可以在控制台中看到路径仅在指定的 prop important
更改时才重新计算。如果您更改任何其他道具,则 path
不会重新计算。
一个更优雅的选择是使用 useState
的回调形式。如果你给它传递一个函数,这个函数只会在需要计算初始状态时被调用——也就是说,在初始渲染时:
const [path, setPath] = React.useState(heavyCalculation);
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'));
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div class="react"></div>
如您在上面的代码片段中所见,doing heavy calculation
仅记录一次。