我怎样才能避免 React/JSX 厄运金字塔?
How can I avoid the React/JSX Pyramid of Doom?
我喜欢在我的 React 和 React Native 项目中使用上下文,这意味着每个项目有多个上下文提供程序。因此,我的应用程序的根通常如下所示:
<ContextA.Provider value={valueA}>
<ContextB.Provider value={valueB}>
<ContextC.Provider value={valueC}>
// ...and so on until rendering actual app content
</ContextC.Provider>
</ContextB.Provider>
</ContextA.Provider>
这创建了一个看起来和感觉都很糟糕的供应商金字塔 style/practice。
我可以将我的上下文值集中到一个大提供商中:
<BigContext.Provider value={ valueA, valueB, valueC }>
/// app content
</BigContext.Provider>
...但是有几个很好的理由希望将上下文分开 - 主要是防止仅对 valueA
感兴趣的组件在仅 valueB
更改时重新呈现,例如。
即使没有上下文,您仍然可以让来自不同包的提供者堆叠到它们自己的金字塔中。这是我的一个 React Native 应用程序的根目录,例如:
<DataContext.Provider value={data}>
<AppearanceProvider>
<SafeAreaProvider>
<NavigationContainer>
<Tab.Navigator>
// tab screens here
</Tab.Navigator>
</NavigationContainer>
</SafeAreaProvider>
</AppearanceProvider>
</DataContext.Provider>
是否有一种干净的方法来“崩溃”或以某种方式避免这些厄运金字塔?
“清理”所谓的毁灭金字塔并没有任何性能优势。拥有多个级别完全 fine.
在您实施下面的代码之前,也许请确保您不会仅仅因为可以就提供全局级别的上下文。上下文提供程序必须包装在使用它的组件附近,这意味着并不总是在根级别。
也就是说,这是一个简单的高阶组件,它可以包装多个 Provider 以提供 Redux compose
风格 API.
/**
* Check if the object has a property matching the key
* NOTE: Works only for shallow objects.
* @param object
* @param key
* @returns {boolean|boolean}
*/
const hasProperty = (object, key) =>
object ? Object.hasOwnProperty.call(object, key) : false;
const hasProps = (arg) =>
hasProperty(arg, 'provider') && hasProperty(arg, 'props');
export const withContextProviders = (...providers) => (Component) => (props) =>
providers.reduceRight((acc, prov) => {
let Provider = prov;
if (hasProps(prov)) {
Provider = prov.context;
const providerProps = prov.props;
return <Provider {...providerProps}>{acc}</Provider>;
}
return <Provider>{acc}</Provider>;
}, <Component {...props} />);
用法:
const Component = () => {...}
export withContextProviders(ThemeProvider, I8nProvider)(Component)
// OR
export withContextProviders({provider: ThemeProvider, props: {darkMode: true}}, I8nProvider)(Component)
// OR
export withContextProviders(ThemeProvider, {provider: I8nProvider, props: {lang: 'en'}})(Component)
// OR
const providers = [{provider: ThemeProvider, props: {darkMode: true}}, {provider: I8nProvider, props: {lang: 'en'}}]
export withContextProviders(...providers)(Component)
NOTE: This does not make singletons, i.e if two components are wrapped with this HoC, both of them will get their own instance of context. For cases like this, it's recommended to wrap the component with provider at the root level
同样,拥有多个级别完全很好。
我喜欢在我的 React 和 React Native 项目中使用上下文,这意味着每个项目有多个上下文提供程序。因此,我的应用程序的根通常如下所示:
<ContextA.Provider value={valueA}>
<ContextB.Provider value={valueB}>
<ContextC.Provider value={valueC}>
// ...and so on until rendering actual app content
</ContextC.Provider>
</ContextB.Provider>
</ContextA.Provider>
这创建了一个看起来和感觉都很糟糕的供应商金字塔 style/practice。
我可以将我的上下文值集中到一个大提供商中:
<BigContext.Provider value={ valueA, valueB, valueC }>
/// app content
</BigContext.Provider>
...但是有几个很好的理由希望将上下文分开 - 主要是防止仅对 valueA
感兴趣的组件在仅 valueB
更改时重新呈现,例如。
即使没有上下文,您仍然可以让来自不同包的提供者堆叠到它们自己的金字塔中。这是我的一个 React Native 应用程序的根目录,例如:
<DataContext.Provider value={data}>
<AppearanceProvider>
<SafeAreaProvider>
<NavigationContainer>
<Tab.Navigator>
// tab screens here
</Tab.Navigator>
</NavigationContainer>
</SafeAreaProvider>
</AppearanceProvider>
</DataContext.Provider>
是否有一种干净的方法来“崩溃”或以某种方式避免这些厄运金字塔?
“清理”所谓的毁灭金字塔并没有任何性能优势。拥有多个级别完全 fine.
在您实施下面的代码之前,也许请确保您不会仅仅因为可以就提供全局级别的上下文。上下文提供程序必须包装在使用它的组件附近,这意味着并不总是在根级别。
也就是说,这是一个简单的高阶组件,它可以包装多个 Provider 以提供 Redux compose
风格 API.
/**
* Check if the object has a property matching the key
* NOTE: Works only for shallow objects.
* @param object
* @param key
* @returns {boolean|boolean}
*/
const hasProperty = (object, key) =>
object ? Object.hasOwnProperty.call(object, key) : false;
const hasProps = (arg) =>
hasProperty(arg, 'provider') && hasProperty(arg, 'props');
export const withContextProviders = (...providers) => (Component) => (props) =>
providers.reduceRight((acc, prov) => {
let Provider = prov;
if (hasProps(prov)) {
Provider = prov.context;
const providerProps = prov.props;
return <Provider {...providerProps}>{acc}</Provider>;
}
return <Provider>{acc}</Provider>;
}, <Component {...props} />);
用法:
const Component = () => {...}
export withContextProviders(ThemeProvider, I8nProvider)(Component)
// OR
export withContextProviders({provider: ThemeProvider, props: {darkMode: true}}, I8nProvider)(Component)
// OR
export withContextProviders(ThemeProvider, {provider: I8nProvider, props: {lang: 'en'}})(Component)
// OR
const providers = [{provider: ThemeProvider, props: {darkMode: true}}, {provider: I8nProvider, props: {lang: 'en'}}]
export withContextProviders(...providers)(Component)
NOTE: This does not make singletons, i.e if two components are wrapped with this HoC, both of them will get their own instance of context. For cases like this, it's recommended to wrap the component with provider at the root level
同样,拥有多个级别完全很好。