Skip to content
Snippets Groups Projects
Commit 124098e5 authored by Ray Schamp's avatar Ray Schamp
Browse files

Refactor App component into reusable HOCs

parent 67885bca
No related branches found
No related tags found
No related merge requests found
import React from 'react';
import ReactDOM from 'react-dom';
import {Provider} from 'react-redux';
import {createStore, applyMiddleware, compose} from 'redux';
import throttle from 'redux-throttle';
import {intlInitialState, IntlProvider} from './reducers/intl.js';
import AppStateHOC from './lib/app-state-hoc.jsx';
import GUI from './containers/gui.jsx';
import log from './lib/log';
import ProjectLoader from './lib/project-loader';
import reducer from './reducers/gui';
import ProjectLoaderHOC from './lib/project-loader-hoc.jsx';
import styles from './index.css';
class App extends React.Component {
constructor (props) {
super(props);
this.fetchProjectId = this.fetchProjectId.bind(this);
this.updateProject = this.updateProject.bind(this);
this.state = {
projectId: null,
projectData: this.fetchProjectId().length ? null : JSON.stringify(ProjectLoader.DEFAULT_PROJECT_DATA)
};
}
componentDidMount () {
window.addEventListener('hashchange', this.updateProject);
this.updateProject();
}
componentWillUnmount () {
window.removeEventListener('hashchange', this.updateProject);
}
fetchProjectId () {
return window.location.hash.substring(1);
}
updateProject () {
const projectId = this.fetchProjectId();
if (projectId !== this.state.projectId) {
if (projectId.length < 1) {
return this.setState({
projectId: projectId,
projectData: JSON.stringify(ProjectLoader.DEFAULT_PROJECT_DATA)
});
}
ProjectLoader.load(projectId, (err, body) => {
if (err) return log.error(err);
this.setState({projectData: body});
});
this.setState({projectId: projectId});
}
}
render () {
if (this.state.projectData === null) return null;
return (
<GUI
projectData={this.state.projectData}
/>
);
}
}
const App = AppStateHOC(ProjectLoaderHOC(GUI));
const appTarget = document.createElement('div');
appTarget.className = styles.app;
document.body.appendChild(appTarget);
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const enhancer = composeEnhancers(
applyMiddleware(
throttle(300, {leading: true, trailing: true})
)
);
const store = createStore(reducer, intlInitialState, enhancer);
ReactDOM.render((
<Provider store={store}>
<IntlProvider>
<App />
</IntlProvider>
</Provider>
), appTarget);
ReactDOM.render(<App />, appTarget);
import React from 'react';
import {Provider} from 'react-redux';
import {createStore, applyMiddleware, compose} from 'redux';
import throttle from 'redux-throttle';
import {intlInitialState, IntlProvider} from '../reducers/intl.js';
import reducer from '../reducers/gui';
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const enhancer = composeEnhancers(
applyMiddleware(
throttle(300, {leading: true, trailing: true})
)
);
const store = createStore(reducer, intlInitialState, enhancer);
/*
* Higher Order Component to provide redux state
* @param {React.Component} WrappedComponent - component to provide state for
* @returns {React.Component} component with redux and intl state provided
*/
const AppStateHOC = function (WrappedComponent) {
const AppStateWrapper = ({...props}) => (
<Provider store={store}>
<IntlProvider>
<WrappedComponent {...props} />
</IntlProvider>
</Provider>
);
return AppStateWrapper;
};
export default AppStateHOC;
import React from 'react';
import xhr from 'xhr';
import log from './log';
import emptyProject from './empty-project.json';
class ProjectLoaderConstructor {
get DEFAULT_PROJECT_DATA () {
return emptyProject;
}
load (id, callback) {
callback = callback || (err => log.error(err));
xhr({
uri: `https://projects.scratch.mit.edu/internalapi/project/${id}/get/`
}, (err, res, body) => {
if (err) return callback(err);
callback(null, body);
});
}
}
const ProjectLoader = new ProjectLoaderConstructor();
/* Higher Order Component to provide behavior for loading projects by id from
* the window's hash (#this part in the url)
* @param {React.Component} WrappedComponent component to receive projectData prop
* @returns {React.Component} component with project loading behavior
*/
const ProjectLoaderHOC = function (WrappedComponent) {
class ProjectLoaderComponent extends React.Component {
constructor (props) {
super(props);
this.fetchProjectId = this.fetchProjectId.bind(this);
this.updateProject = this.updateProject.bind(this);
this.state = {
projectId: null,
projectData: this.fetchProjectId().length ? null : JSON.stringify(ProjectLoader.DEFAULT_PROJECT_DATA)
};
}
componentDidMount () {
window.addEventListener('hashchange', this.updateProject);
this.updateProject();
}
componentWillUnmount () {
window.removeEventListener('hashchange', this.updateProject);
}
fetchProjectId () {
return window.location.hash.substring(1);
}
updateProject () {
const projectId = this.fetchProjectId();
if (projectId !== this.state.projectId) {
if (projectId.length < 1) {
return this.setState({
projectId: projectId,
projectData: JSON.stringify(ProjectLoader.DEFAULT_PROJECT_DATA)
});
}
ProjectLoader.load(projectId, (err, body) => {
if (err) return log.error(err);
this.setState({projectData: body});
});
this.setState({projectId: projectId});
}
}
render () {
if (!this.state.projectData) return null;
return (
<WrappedComponent
projectData={this.state.projectData}
{...this.props}
/>
);
}
}
return ProjectLoaderComponent;
};
export {
ProjectLoaderHOC as default,
ProjectLoader
};
import xhr from 'xhr';
import log from './log';
import emptyProject from './empty-project.json';
class ProjectLoader {
constructor () {
this.DEFAULT_PROJECT_DATA = ProjectLoader.DEFAULT_PROJECT_DATA;
}
load (id, callback) {
callback = callback || (err => log.error(err));
xhr({
uri: `https://projects.scratch.mit.edu/internalapi/project/${id}/get/`
}, (err, res, body) => {
if (err) return callback(err);
callback(null, body);
});
}
}
ProjectLoader.DEFAULT_PROJECT_DATA = emptyProject;
export default new ProjectLoader();
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment