Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hot reloading is not working #16

Open
dnish opened this issue Mar 13, 2017 · 5 comments
Open

Hot reloading is not working #16

dnish opened this issue Mar 13, 2017 · 5 comments

Comments

@dnish
Copy link

dnish commented Mar 13, 2017

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.

@reyalpsirc
Copy link

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?

@reyalpsirc
Copy link

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
    }
  }
}

@filipepratalima
Copy link

This is really a big missing feature, cuts a lot of development time having to manually reload the screen every time.

@patrickgalbraith
Copy link

patrickgalbraith commented Sep 19, 2017

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 GeekyAnts#1

@huyphamwork
Copy link

Hello, anyone found the way to resolve this issue?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants