样式组件中的动态主题
Dynamic Theme in Styled Components
我在我的 React 应用程序中使用样式化组件并希望使用动态主题。有些区域会使用我的深色主题,有些区域会使用浅色。因为样式组件必须在使用它们的组件之外声明,我们如何动态地传递主题?
这正是 ThemeProvider
组件的用途!
您的样式组件在插入函数时可以访问特殊的 theme
道具:
const Button = styled.button`
background: ${props => props.theme.primary};
`
此 <Button />
组件现在将动态响应由 ThemeProvider
定义的主题。你如何定义一个主题?将任何对象传递给 ThemeProvider
:
的 theme
道具
const theme = {
primary: 'palevioletred',
};
<ThemeProvider theme={theme}>
<Button>I'm now palevioletred!</Button>
</ThemeProvider>
我们通过 context
为您的样式组件提供主题,这意味着无论组件和 ThemeProvider 之间有多少组件或 DOM 节点,它的工作方式仍然完全相同:
const theme = {
primary: 'palevioletred',
};
<ThemeProvider theme={theme}>
<div>
<SidebarContainer>
<Sidebar>
<Button>I'm still palevioletred!</Button>
</Sidebar>
</SidebarContainer>
</div>
</ThemeProvider>
这意味着您可以将整个应用程序包装在一个 ThemeProvider
中,并且所有样式化的组件都将采用该主题。您可以动态交换 属性 以在浅色和深色主题之间切换!
您可以根据需要在您的应用中添加任意数量的 ThemeProvider
。大多数应用程序只需要一个来包装整个应用程序,但要让您的应用程序的一部分为浅色主题,而其他部分为深色主题,您只需将它们包装在两个具有不同主题的 ThemeProvider
中:
const darkTheme = {
primary: 'black',
};
const lightTheme = {
primary: 'white',
};
<div>
<ThemeProvider theme={lightTheme}>
<Main />
</ThemeProvider>
<ThemeProvider theme={darkTheme}>
<Sidebar />
</ThemeProvider>
</div>
Main
内任意位置的任何样式组件现在都将采用浅色主题,而 Sidebar
内任意位置的任何样式组件都将采用深色主题。它们会根据呈现的应用程序区域进行调整,您无需执行任何操作即可实现!
我鼓励您查看我们的 docs about theming,因为 styled-components 在构建时考虑到了这一点。
在 styled-components 存在之前,JS 中样式的一大痛点是以前的库对样式的封装和托管做得很好,但是 none 其中有适当的主题支持。如果您想了解更多关于我们现有库的其他痛点,我鼓励您观看 my talk at ReactNL 我发布样式组件的地方。 (注意:styled-components 的第一次出现是在~25 分钟后,不要惊讶!)
虽然这个问题本来是为了同时有多个主题运行,但我个人想在运行时动态切换一个整个应用程序的单一主题。
我是这样实现的:(我将在这里使用 TypeScript 和钩子。对于普通的 JavaScript,只需删除 type
s、as
和 interface
):
为了以防万一,我还在每个块代码的顶部包含了所有导入。
我们定义我们的 theme.ts
文件
//theme.ts
import baseStyled, { ThemedStyledInterface } from 'styled-components';
export const lightTheme = {
all: {
borderRadius: '0.5rem',
},
main: {
color: '#FAFAFA',
textColor: '#212121',
bodyColor: '#FFF',
},
secondary: {
color: '#757575',
},
};
// Force both themes to be consistent!
export const darkTheme: Theme = {
// Make properties the same on both!
all: { ...lightTheme.all },
main: {
color: '#212121',
textColor: '#FAFAFA',
bodyColor: '#424242',
},
secondary: {
color: '#616161',
},
};
export type Theme = typeof lightTheme;
export const styled = baseStyled as ThemedStyledInterface<Theme>;
然后在我们的主要条目中,在这种情况下 App.tsx
我们在将要使用主题的每个组件之前定义 <ThemeProvider>
。
// app.tsx
import React, { memo, Suspense, lazy, useState } from 'react';
import { Router } from '@reach/router';
// The header component that switches the styles.
import Header from './components/header';
// Personal component
import { Loading } from './components';
import { ThemeProvider } from 'styled-components';
// Bring either the lightTheme, or darkTheme, whichever you want to make the default
import { lightTheme } from './components/styles/theme';
// Own code.
const Home = lazy(() => import('./views/home'));
const BestSeller = lazy(() => import('./views/best-seller'));
/**
* Where the React APP main layout resides:
*/
function App() {
// Here we set the default theme of the app. In this case,
// we are setting the lightTheme. If you want the dark, import the `darkTheme` object.
const [theme, setTheme] = useState(lightTheme);
return (
<Suspense fallback={<Loading />}>
<ThemeProvider theme={theme}>
<React.Fragment>
{/* We pass the setTheme function (lift state up) to the Header */}
<Header setTheme={setTheme} />
<Router>
<Home path="/" />
<BestSeller path="/:listNameEncoded" />
</Router>
</React.Fragment>
</ThemeProvider>
</Suspense>
);
}
export default memo(App);
并且在header.tsx中我们将setTheme传递给组件(提升状态):
// header.tsx
import React, { memo, useState } from 'react';
import styled, { ThemedStyledInterface } from 'styled-components';
import { Theme, lightTheme, darkTheme } from '../styles/theme';
// We have nice autocomplete functionality
const Nav = styled.nav`
background-color: ${props => props.theme.colors.primary};
`;
// We define the props that will receive the setTheme
type HeaderProps = {
setTheme: React.Dispatch<React.SetStateAction<Theme>>;
};
function Header(props:
function setLightTheme() {
props.setTheme(lightTheme);
}
function setDarkTheme() {
props.setTheme(darkTheme);
}
// We then set the light or dark theme according to what we want.
return (
<Nav>
<h1>Book App</h1>
<button onClick={setLightTheme}>Light </button>
<button onClick={setDarkTheme}> Dark </button>
</Nav>
);
}
export default memo(Header);
这是对我有用的东西:
import * as React from 'react';
import { connect } from 'react-redux';
import { getStateField } from 'app/redux/reducers/recordings';
import { lightTheme, darkTheme, ThemeProvider as SCThemeProvider } from 'app/utils/theme';
import { GlobalStyle } from 'app/utils/globalStyles';
interface ThemeProviderProps {
children: JSX.Element;
isLightMode?: boolean;
}
const ThemeProvider = ({ children, isLightMode }: ThemeProviderProps) => {
return (
<SCThemeProvider theme={isLightMode ? lightTheme : darkTheme}>
<React.Fragment>
{children}
<GlobalStyle />
</React.Fragment>
</SCThemeProvider>
);
};
export const ConnectedThemeProvider = connect((state) => ({
isLightMode: getStateField('isLightMode', state)
}))(ThemeProvider);
我在我的 React 应用程序中使用样式化组件并希望使用动态主题。有些区域会使用我的深色主题,有些区域会使用浅色。因为样式组件必须在使用它们的组件之外声明,我们如何动态地传递主题?
这正是 ThemeProvider
组件的用途!
您的样式组件在插入函数时可以访问特殊的 theme
道具:
const Button = styled.button`
background: ${props => props.theme.primary};
`
此 <Button />
组件现在将动态响应由 ThemeProvider
定义的主题。你如何定义一个主题?将任何对象传递给 ThemeProvider
:
theme
道具
const theme = {
primary: 'palevioletred',
};
<ThemeProvider theme={theme}>
<Button>I'm now palevioletred!</Button>
</ThemeProvider>
我们通过 context
为您的样式组件提供主题,这意味着无论组件和 ThemeProvider 之间有多少组件或 DOM 节点,它的工作方式仍然完全相同:
const theme = {
primary: 'palevioletred',
};
<ThemeProvider theme={theme}>
<div>
<SidebarContainer>
<Sidebar>
<Button>I'm still palevioletred!</Button>
</Sidebar>
</SidebarContainer>
</div>
</ThemeProvider>
这意味着您可以将整个应用程序包装在一个 ThemeProvider
中,并且所有样式化的组件都将采用该主题。您可以动态交换 属性 以在浅色和深色主题之间切换!
您可以根据需要在您的应用中添加任意数量的 ThemeProvider
。大多数应用程序只需要一个来包装整个应用程序,但要让您的应用程序的一部分为浅色主题,而其他部分为深色主题,您只需将它们包装在两个具有不同主题的 ThemeProvider
中:
const darkTheme = {
primary: 'black',
};
const lightTheme = {
primary: 'white',
};
<div>
<ThemeProvider theme={lightTheme}>
<Main />
</ThemeProvider>
<ThemeProvider theme={darkTheme}>
<Sidebar />
</ThemeProvider>
</div>
Main
内任意位置的任何样式组件现在都将采用浅色主题,而 Sidebar
内任意位置的任何样式组件都将采用深色主题。它们会根据呈现的应用程序区域进行调整,您无需执行任何操作即可实现!
我鼓励您查看我们的 docs about theming,因为 styled-components 在构建时考虑到了这一点。
在 styled-components 存在之前,JS 中样式的一大痛点是以前的库对样式的封装和托管做得很好,但是 none 其中有适当的主题支持。如果您想了解更多关于我们现有库的其他痛点,我鼓励您观看 my talk at ReactNL 我发布样式组件的地方。 (注意:styled-components 的第一次出现是在~25 分钟后,不要惊讶!)
虽然这个问题本来是为了同时有多个主题运行,但我个人想在运行时动态切换一个整个应用程序的单一主题。
我是这样实现的:(我将在这里使用 TypeScript 和钩子。对于普通的 JavaScript,只需删除 type
s、as
和 interface
):
为了以防万一,我还在每个块代码的顶部包含了所有导入。
我们定义我们的 theme.ts
文件
//theme.ts
import baseStyled, { ThemedStyledInterface } from 'styled-components';
export const lightTheme = {
all: {
borderRadius: '0.5rem',
},
main: {
color: '#FAFAFA',
textColor: '#212121',
bodyColor: '#FFF',
},
secondary: {
color: '#757575',
},
};
// Force both themes to be consistent!
export const darkTheme: Theme = {
// Make properties the same on both!
all: { ...lightTheme.all },
main: {
color: '#212121',
textColor: '#FAFAFA',
bodyColor: '#424242',
},
secondary: {
color: '#616161',
},
};
export type Theme = typeof lightTheme;
export const styled = baseStyled as ThemedStyledInterface<Theme>;
然后在我们的主要条目中,在这种情况下 App.tsx
我们在将要使用主题的每个组件之前定义 <ThemeProvider>
。
// app.tsx
import React, { memo, Suspense, lazy, useState } from 'react';
import { Router } from '@reach/router';
// The header component that switches the styles.
import Header from './components/header';
// Personal component
import { Loading } from './components';
import { ThemeProvider } from 'styled-components';
// Bring either the lightTheme, or darkTheme, whichever you want to make the default
import { lightTheme } from './components/styles/theme';
// Own code.
const Home = lazy(() => import('./views/home'));
const BestSeller = lazy(() => import('./views/best-seller'));
/**
* Where the React APP main layout resides:
*/
function App() {
// Here we set the default theme of the app. In this case,
// we are setting the lightTheme. If you want the dark, import the `darkTheme` object.
const [theme, setTheme] = useState(lightTheme);
return (
<Suspense fallback={<Loading />}>
<ThemeProvider theme={theme}>
<React.Fragment>
{/* We pass the setTheme function (lift state up) to the Header */}
<Header setTheme={setTheme} />
<Router>
<Home path="/" />
<BestSeller path="/:listNameEncoded" />
</Router>
</React.Fragment>
</ThemeProvider>
</Suspense>
);
}
export default memo(App);
并且在header.tsx中我们将setTheme传递给组件(提升状态):
// header.tsx
import React, { memo, useState } from 'react';
import styled, { ThemedStyledInterface } from 'styled-components';
import { Theme, lightTheme, darkTheme } from '../styles/theme';
// We have nice autocomplete functionality
const Nav = styled.nav`
background-color: ${props => props.theme.colors.primary};
`;
// We define the props that will receive the setTheme
type HeaderProps = {
setTheme: React.Dispatch<React.SetStateAction<Theme>>;
};
function Header(props:
function setLightTheme() {
props.setTheme(lightTheme);
}
function setDarkTheme() {
props.setTheme(darkTheme);
}
// We then set the light or dark theme according to what we want.
return (
<Nav>
<h1>Book App</h1>
<button onClick={setLightTheme}>Light </button>
<button onClick={setDarkTheme}> Dark </button>
</Nav>
);
}
export default memo(Header);
这是对我有用的东西:
import * as React from 'react';
import { connect } from 'react-redux';
import { getStateField } from 'app/redux/reducers/recordings';
import { lightTheme, darkTheme, ThemeProvider as SCThemeProvider } from 'app/utils/theme';
import { GlobalStyle } from 'app/utils/globalStyles';
interface ThemeProviderProps {
children: JSX.Element;
isLightMode?: boolean;
}
const ThemeProvider = ({ children, isLightMode }: ThemeProviderProps) => {
return (
<SCThemeProvider theme={isLightMode ? lightTheme : darkTheme}>
<React.Fragment>
{children}
<GlobalStyle />
</React.Fragment>
</SCThemeProvider>
);
};
export const ConnectedThemeProvider = connect((state) => ({
isLightMode: getStateField('isLightMode', state)
}))(ThemeProvider);