theme
theme copied to clipboard
Hot reloading is not working
Hey, I'm having an issue with my custom theme and hot reloading. If I change some values of my theme, f.e. the backgroundColor, I see a "Hot reloading..." message on my phones screen, but the changes are not reflected until I do a full reload of my app. Is there any workaround for this issue? It would be very nice for our workflow if we could see changes without doing a full reload of our app.
Same thing is happening to me. Is there any workaround which can keep the state so that I can keep the same page visible with the same data while changing the design of it?
Okay, I've been able to tweak some code in order to have the Hot Reloading working. Please note that I'm not sure if this is the best way but, at least it works for me.
For this to work, you'll need to have your styles in separate files and require each one of them on the theme.js file inside the buildTheme
function. You can see an example of one of those files at the end of this comment.
Also, each Component will need to use connectStyleHotReload
of the theme.js file instead of the default connectStyle
.
theme.js:
import { connectStyle, Theme } from '@shoutem/theme'
import hoistStatics from 'hoist-non-react-statics'
import { getTheme } from '@shoutem/ui'
import React, {Component} from 'react'
function buildTheme () {
return Object.assign({}, getTheme(), {
// Set your theme styles here using require
'containers.SplashscreenPage': { ...(require('./styles/containers/#SplashscreenPage').default)(commonStyles) },
'components.DefaultButton': { ...(require('./styles/components/#DefaultButton').default)(commonStyles) }
})
}
var commonStyles = {
// If you have some common styles to use accross multiple components, define them here and call them on the files
mainAppActiveColor: {
backgroundColor: '#4286f4'
},
navigationBar: {
alignSelf: 'stretch',
backgroundColor: '#CCCCCC'
},
navigationBarTile: {
color: 'black',
fontSize: 18,
marginTop: 7
}
}
if (!window.themeData) {
window.themeData = buildTheme()
}
var theme = window.themeData
function checkIfThemeKeyChanged (keyName, newValue) {
return !(JSON.stringify(newValue) === JSON.stringify(window.themeData[keyName]))
}
if (module.hot) {
if (!window.themeCallbacks) {
window.themeCallbacks = {}
}
module.hot.accept(() => {
var newTheme = buildTheme()
Theme.setDefaultThemeStyle(newTheme)
var keys = Object.keys(newTheme)
for (var i = 0; i < keys.length; i++) {
var keyName = keys[i]
var hasChanged = checkIfThemeKeyChanged(keyName, newTheme[keyName])
if (hasChanged) {
if (window.themeCallbacks[keyName]) {
for (var k = 0; k < window.themeCallbacks[keyName].length; k++) {
window.themeCallbacks[keyName][k]()
}
}
}
}
window.themeData = newTheme
theme = window.themeData
})
}
function registerCallback (componentStyleName, callback) {
if (module.hot) {
if (!window.themeCallbacks[componentStyleName]) {
window.themeCallbacks[componentStyleName] = []
}
var position = window.themeCallbacks[componentStyleName].length
window.themeCallbacks[componentStyleName].push(callback)
return position
}
return null
}
function unregisterCallback (componentStyleName, callbackPosition) {
if (module.hot) {
if (window.themeCallbacks[componentStyleName] && callbackPosition < window.themeCallbacks[componentStyleName].length) {
window.themeCallbacks[componentStyleName].splice(callbackPosition, 1)
}
}
}
function connectStyleHotReload (componentStyleName, componentStyle = {}, mapPropsToStyleNames, options = {}) {
function getComponentDisplayName (WrappedComponent) {
return WrappedComponent.displayName || WrappedComponent.name || 'Component'
}
return (WrappedComponent) => {
const componentDisplayName = getComponentDisplayName(WrappedComponent)
var BoundComponent = connectStyle(componentStyleName, componentStyle, mapPropsToStyleNames, options)(WrappedComponent)
class CustomStyledComponent extends Component {
static displayName = `Styled(${componentDisplayName})`;
static WrappedComponent = WrappedComponent;
componentWillMount () {
this.callbackPosition = registerCallback(componentStyleName, () => {
if (this.refs.boundInst) {
this.refs.boundInst.context.theme = Theme.getDefaultTheme()
var nextContext = this.refs.boundInst.context
var nextProps = this.refs.boundInst.props
var styleNames = this.refs.boundInst.resolveStyleNames(nextProps)
const resolvedStyle = this.refs.boundInst.resolveStyle(nextContext, nextProps, styleNames)
this.refs.boundInst.setState({
...this.refs.boundInst.state,
style: resolvedStyle.componentStyle,
childrenStyle: resolvedStyle.childrenStyle
})
} else {
console.log('Hot reload failed. Please use "connectStyleHotReload" on Component "' + componentDisplayName + '"')
}
})
}
componentWillUnmount () {
unregisterCallback(componentStyleName, this.callbackPosition)
}
render () {
return (
<BoundComponent
ref='boundInst'
{...this.props}
/>)
}
}
return hoistStatics(CustomStyledComponent, WrappedComponent)
}
}
export {
theme,
connectStyleHotReload,
commonStyles
}
Example of style file (#DefaultButton.js):
export default (commonStyles) => {
return {
button: {
borderRadius: 10
},
enabled: {
...commonStyles.mainAppActiveColor
},
disabled: {
...commonStyles.mainAppActiveColor
},
text: {
fontSize: 12
}
}
}
This is really a big missing feature, cuts a lot of development time having to manually reload the screen every time.
This is a big issue for me since I need to dynamically reload styles in my app in certain cases so it isn't just an issue during development.
There is a patch here which works https://github.com/GeekyAnts/theme/pull/1
Hello, anyone found the way to resolve this issue?