import React from 'react';
/**
* The theme components only imports it's theme CSS-file. These components are lazy
* loaded, to enable "code splitting" (in order to avoid the themes being bundled together)
*/
const Theme1 = React.lazy(() => import('./Theme1'));
const Theme2 = React.lazy(() => import('./Theme2'));
const ThemeSelector: React.FC = ({ children }) => (
<>
{/* Conditionally render theme, based on the current client context */}
<React.Suspense fallback={() => null}>
{shouldRenderTheme1 && <Theme1 />}
{shouldRenderTheme2 && <Theme2 />}
</React.Suspense>
{/* Render children immediately! */}
{children}
</>
);
export default ThemeSelector;
Theme组件的唯一任务是导入正确的css文件:
import * as React from 'react';
// 👇 Only important line - as this component should be lazy-loaded,
// to enable code - splitting for this CSS.
import 'theme1.css';
const Theme1: React.FC = () => <></>;
export default Theme1;
ThemeSelector应该将App组件 Package 在src/index.tsx中:
import React from 'react';
import ReactDOM from 'react-dom';
import ThemeSelector from 'themes/ThemeSelector';
ReactDOM.render(
<ThemeSelector>
<App />
</ThemeSelector>,
document.getElementById('root')
);
据我所知,这迫使每个Theme被拆分成单独的bundle(有效地也拆分了CSS)。 正如在评论中提到的,这个解决方案并没有提供一种简单的运行时切换主题的方法。此解决方案侧重于将主题拆分为单独的捆绑包。 如果您已经将主题拆分为单独的CSS文件,并且希望在运行时交换主题,则可能需要使用ReactHelmet(illustrated by @Alexander Ladonin's answer below)来查看解决方案。
import React, {
useEffect, createContext, useState, useContext,
} from 'react';
import { Nullable } from 'types';
// Import both files here like this:
// Import of CSS file number 1
import LightMode from './theme/styles.lazy.css';
// Import of CSS file number 2
import DarkMode from './theme/styles.lazy.css';
interface IContext {
theme: Nullable<string>
toggleTheme: () => void
}
const Context = createContext<IContext>({
theme: null,
toggleTheme: () => { },
});
// Your Provider component that returns the Context.Provider
// Let's also play with the sessionStorage, so this state doesn't
// brake with browser refresh or logouts
const ThemeProvider: React.FC = ({ children }) => {
// Im initialazing here the state with any existing value in the
//sessionStorage, or not...
const [theme, setTheme] = useState<Nullable<string>>(sessionStorage.getItem('themeMode') || 'dark');
// this setter Fn we can pass down to anywhere
const toggleTheme = () => {
const newThemeValue = theme === 'dark' ? 'light' : 'dark';
setTheme(newThemeValue);
sessionStorage.setItem('themeMode', newThemeValue);
};
// Now the magic, this lazy css files you can use or unuse
// This is exactly what you need, import the CSS but also unimport
// the one you had imported before. An actual toggle of import in a
// dynamic way.. brought to you by webpack
useEffect(() => {
if (theme === 'light') {
DarkMode.unuse();
LightMode.use();
} else if (theme == 'dark') {
LightMode.unuse();
DarkMode.use();
}
}, [theme]);
return (
<Context.Provider value={{ theme, toggleTheme }}>
{children}
</Context.Provider>
);
};
export default ThemeProvider;
// This useTheme hook will give you the context anywhere to set the state of // theme and this will toggle the styles imported
export const useTheme = () => useContext(Context);
import { useTheme } from './yourContextFile';
// inside your component
const AnyComponentDownTheTree = () => {
const { theme, toggleTheme } = useTheme()
// use the toggleTheme function to toggle and the theme actual value
// for your components, you might need disable something or set active a
// switch, etc, etc
}
8条答案
按热度按时间kgsdhlau1#
require
方法只在开发中起作用(因为所有的CSS都是在构建时捆绑的),而import
方法根本不起作用(使用CRA版本3.3)。在我们的例子中,我们有多个主题,不能捆绑-所以我们使用
React.lazy
和React.Suspense
解决了这个问题。我们有
ThemeSelector
,它有条件地加载正确的css。Theme
组件的唯一任务是导入正确的css文件:ThemeSelector
应该将App
组件 Package 在src/index.tsx
中:据我所知,这迫使每个
Theme
被拆分成单独的bundle(有效地也拆分了CSS)。正如在评论中提到的,这个解决方案并没有提供一种简单的运行时切换主题的方法。此解决方案侧重于将主题拆分为单独的捆绑包。
如果您已经将主题拆分为单独的CSS文件,并且希望在运行时交换主题,则可能需要使用
ReactHelmet
(illustrated by @Alexander Ladonin's answer below)来查看解决方案。zaqlnxep2#
您可以使用
require('file.css')
语法代替。这将允许你把它放在条件句中。例如
gwo2fgha3#
使用React Helmet。它动态地添加链接,Meta标签等到 * 文档头 *。将其添加到任何渲染方法中。
你可以在下一个
<ReactHelmet/>
渲染中重写它。hwamh0ep4#
我发现一个在生产环境中有效的简单解决方案是使用vercel的styled-jsx。首先,安装styled-jsx:
如果你使用Yarn:
现在从你的css文件中创建字符串,例如:
然后在你的React组件中,你可以这样做:
简单地将
<style jsx>{your_css_string}</style>
添加到你想要添加样式的组件中,然后你可以使用不同的字符串来导入不同的css样式来实现条件。wpx232ag5#
如果你在这里,你最有可能是试图条件的CSS或SCSS导入,可能是使一些光/暗模式的主题或东西。接受的答案只在挂载时起作用,在第二个css加载后,它们都被加载了,你没有办法卸载它们,或者实际上你有,继续阅读.
使用React lazy和suspense是很棒的,但在这种情况下,我们需要从webpack中帮助我们自己,因为实际上是捆绑东西的人,也可以解包东西,这就是你需要的,基本上是css导入的切换
添加webpack lazyStyleTag
转到您的webpack配置文件并添加以下规则
现在,将
CSS
文件的名称更改为lazy
命名约定你可能有这个
现在会是这样:
然后在一个简单的React上下文中创建你的React主题Provider,这个上下文将 Package 你的App,这样每当上下文状态改变时,它就会加载有条件的CSS。这个上下文状态将在你的应用程序中的任何地方都可用,以及通过一个自定义钩子的setter,我们将从同一个文件导出,看看这个:
请记住将此状态放在sessionStorage上(如本例所示),以便用户在每次返回或刷新页面时都可以使用此状态
别忘了用提供商 Package 这个该死的应用程序:
现在只需使用很酷的
useTheme
钩子来切换应用程序的CSS导入mum43rcc6#
我在一些教程中测试了一些可用的替代方法,对我来说最好的方法是只使用css中的类。
我在使用时遇到的一个问题是,
要求:在某些情况下没有覆盖
import:加载css产生的延迟
对我来说最好的方法就是把一个类切换
'
vawmfj5a7#
我通过添加一个有条件呈现的兄弟
div
,然后利用css selector "+"来定位条件内容,解决了这个问题。下面是一个示例:现在文本的颜色默认为黑色,但只有当
condition === true
时才会更新为红色。这是一个有点黑客,但工程为我的简单应用程序。8xiog9wr8#
其他解决方案对我不起作用。经过一天的搜索,我得到了下面的解决方案。在我的问题中,我有两个用于RTL或LTR的CSS文件,如
app.rtl.css
或app.ltr.css
创建一个功能组件
Style
,如下所示:然后你可以调用它,例如在
App.js
中:direction
param包含rtl
或ltr
,并确定应加载哪个文件。