I would like to understand whether it is possible to change the theme value using ThemeProvider by getting an input from the user and managing state.
I have the app that is wrapped in a ThemeProvider to manage the theme.
import styled from 'styled-components'
import { ThemeProvider } from 'styled-components'
import appTheme from './theme'
function App(props, theme) {
// const themeContext = useContext(ThemeContext)
const [themePrimary, setThemePrimary] = useState(appTheme.primary)
function changePrimaryColor(props) {
setThemePrimary(props)
appTheme.primary = props
}
...
return (
<ThemeProvider theme={theme}>
<Router>
<Switch>
<Route
path="/"
exact
render={({ match }) => {
return (
<Frame id="app-frame">
<AppHeader match={match} />
<Main>
<p>Home</p>
</Main>
</Frame>
)
}}
/>
...
In the settings view the user must be able to enter a color value.
const ColorTest = styled.input`
background: ${props => theme.primary};
border: none;
margin-left: 50px;
`
...
const colorInputValue = useRef(null)
const handleColorSubmit = (e) => {
e.preventDefault()
}
const handleClick = () => {
const color = colorInputValue.current.value
props.changePrimaryColor(color)
console.log(color)
}
...
<PageSection>
<Form onSubmit={handleColorSubmit}>
<h2>Change primary color</h2>
<ColorInput placeholder="hex or string" ref={colorInputValue} />
<SubmitFormBtn type="submit" onClick={handleClick}>Change</SubmitFormBtn>
<ColorTest disabled></ColorTest>
</Form>
</PageSection>
(The ColorTest component here is for testing purposes. It actually shows me that all the data is being passed correctly and the color changes.)
Here is my "theme"
const theme = {
primary: '#0068B6',
secondary: '',
text_color: '#555',
text_light: '#fff',
border: '#e3e3e3',
drop_shadow: '3px 3px 3px rgba(0, 0, 0, 0.3)',
background: '#fff',
background_secondary: '#f5f5f5',
}
export default theme
That new value must change the color value of the primary color and trigger the app component to re-render. How do I make that happen without using local storage or database, but just state?
You can try something like the following. You can now pass down the updateTheme
function to other child components.
I'd potentially take the defaultTheme
out of the component and pass it in as a prop (not an imported file). This would mean you could reuse the App
component with several different default themes.
import React, { useState } from 'react';
import styled from 'styled-components';
import { ThemeProvider } from 'styled-components';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'; // Not sure if this is your import
export default function App({
defaultTheme={
primary: '#0068B6',
secondary: '',
text_color: '#555',
text_light: '#fff',
border: '#e3e3e3',
drop_shadow: '3px 3px 3px rgba(0, 0, 0, 0.3)',
background: '#fff',
background_secondary: '#f5f5f5',
},
}) {
const [theme, setTheme] = useState(defaultTheme);
// update here is an object, e.g., { primary: '#333' }
const updateTheme = (update) => {
return setTheme({ ...theme, ...update });
};
const paint = () => {
// If you're using something to generate the theme, you can put it here instead. For example () => createMuiTheme(theme).
return React.useMemo(() => ({ ...theme }), [theme]);
};
return (
<ThemeProvider theme={paint()}>
<Router>
<Switch>
<Route
exact
path='/'
render={() => <View theme={theme} updateTheme={updateTheme} />}
/>
</Switch>
</Router>
</ThemeProvider>
);
};
// Or use your hooks to get the theme
export default function View({ updateTheme, theme }) {
return (
<>
<div
style={{
backgroundColor: theme.primary,
height: 100,
width: 100,
}}
/>
<button
onClick={() => updateTheme({ primary: '#333' })}
>
Change primary
</button>
</>
);
};