diff --git a/loaders/examples.loader.js b/loaders/examples.loader.js index 9a84d0979..f573f1826 100644 --- a/loaders/examples.loader.js +++ b/loaders/examples.loader.js @@ -101,9 +101,9 @@ function examplesLoader(source, map) { '}', 'module.exports = ' + JSON.stringify(examples).replace( new RegExp(_.escapeRegExp(JSON.stringify(evalPlaceholder)), 'g'), - 'function(code, setState) {' + - ' var require = requireInRuntime;' + - ' return eval(code);' + + 'function(code) {' + + ' var func = new Function (\"require\", \"state\", \"setState\", \"__initialStateCB\", code);' + + ' return func.bind(null, requireInRuntime);' + '}' ) + ';' ].join('\n'); diff --git a/src/rsg-components/Preview/Preview.js b/src/rsg-components/Preview/Preview.js index a92f63806..d0fb80028 100644 --- a/src/rsg-components/Preview/Preview.js +++ b/src/rsg-components/Preview/Preview.js @@ -31,11 +31,6 @@ export default class Preview extends Component { } } - setComponentState(newState) { - this.componentState = { ...this.componentState, ...newState }; - setTimeout(this.executeCode.bind(this), 0); - } - compileCode(code) { return transform(code, { presets: ['es2015', 'react', 'stage-0'], @@ -57,15 +52,58 @@ export default class Preview extends Component { } try { - code = ` - const state = Object.freeze(${JSON.stringify(this.componentState)}); - ${code} + const compiledCode = this.compileCode(this.props.code); + // initiate state and set with the callback in the bottom component; + const initCode = ` + var initialState = {}; + ${compiledCode} + __initialStateCB(initialState); `; - let compiledCode = this.compileCode(code); - let component = this.props.evalInContext(compiledCode, this.setComponentState.bind(this)); - let wrappedComponent = ( + // evalInContext returns a function which takes state, setState and a callback to handle the + // initial state and returns the evaluated code + const initial = this.props.evalInContext(initCode); + + // 1) setup initialState so that we don't get an error; + // 2) use require data or make other setup for the example component; + // 3) return the example component + const exampleComponentCode = ` + var initialState = {}; + return eval(${JSON.stringify(compiledCode)}); + `; + + const exampleComponent = this.props.evalInContext(exampleComponentCode); + // wrap everything in a react component, such that we can leverage the state management of this component + class PreviewComponent extends React.Component { // eslint-disable-line react/no-multi-comp + + constructor(props) { + super(props); + + const state = {}; + const initialStateCB = (initialState) => { + Object.assign(state, initialState); + }; + const setStateError = (partialState) => { + const err = 'Calling setState to setup the initial state is deprecated. Use\ninitialState = '; + Object.assign(state, { error: err + JSON.stringify(partialState) + ';' }); + }; + initial({}, setStateError, initialStateCB); + this.state = state; + } + + render() { + if (this.state.error) { + return
{this.state.error}; + } + const setState = (nextState, callback) => this.setState(nextState, callback); + const state = this.state; + // pass through props from the wrapper component + return React.cloneElement(exampleComponent(state, setState, null), this.props); + } + } + + const wrappedComponent = (