Coder Social home page Coder Social logo

ctrlplusb / react-async-bootstrapper Goto Github PK

View Code? Open in Web Editor NEW
118.0 3.0 12.0 1.11 MB

Execute a bootstrap method on your React/Preact components. Useful for data prefetching and other activities.

License: MIT License

JavaScript 100.00%
react ssr server-side-rendering react-dom prefetch

react-async-bootstrapper's Introduction

react-async-bootstrapper 👢

Execute a bootstrap method on your React/Preact components. Useful for data prefetching and other activities.

npm MIT License Travis Codecov

TOCs

Introduction

This library is a simple implementation of react-tree-walker, allowing you to attach a bootstrap method to your React/Preact "class" components. I would highly recommend you review react-tree-walkers documentation so as to gain more familiarity with what is being wrapped up by react-bootstrapper.

I have created this implementation that responds to a bootstrap method to allow me to have a standard implementation that would allow for interop between multiple packages requiring a bootstrapping process. For example I have create react-async-component which provides code splitting support, and react-jobs which enables data fetching. Both packages use this library to allow for a single bootstrapping parse satisfying the needs of both.

Simple Example

import bootstrapper from 'react-async-bootstrapper'

// Our super naive global state. Don't copy this, it's just illustrative. You'd
// likely want to use something
const globalStateManager = {
  products: {},
}

class Product extends Component {
  // 👇
  bootstrap() {
    // Fetch our product and load up our state
    return fetch(`/api/products/${this.props.productId}`).then(response => {
      // store in our global state
      globalStateManager.products[this.props.productId] = response.json()
    })
  }

  render() {
    const product = globalStateManager.products[this.props.productId]
    return (
      <div>
        {product.name} - {product.price}
      </div>
    )
  }
}

const app = (
  <div>
    <h1>My favourite product</h1>
    <Product productId={1337} />
  </div>
)

// Now for the bootstrapping/rendering process (on a client/server)
bootstrapper(app)
  .then(() => {
    // Bootstrapping is complete, now when we render our application to the DOM
    // the global products state will be populated and so our components
    // should render immediately with the data.
    ReactDOM.render(app, document.getElementById('app'))
  })
  .catch(err => console.log('Eek, error!', err))

Yep, not a particularly useful idea in the context of executing on the front end only, but when doing server side rendering of your react application this pattern can be extremely useful.

API

The API is very simple at the moment, only exposing a single function.

bootstrapper

The default export of the library. The function that performs the magic.

const bootstrapper = require('react-async-bootstrapper')

or

import bootstrapper from 'react-async-bootstrapper'

Paramaters

  • app (React/Preact application/element, required)

    The react application you wish to walk.

    e.g. <div>Hello world</div>

  • options (Object, optional)

    Additional options/configuration. It currently supports the following values:

    • componentWillUnmount: Enable this to have the componentWillUnmount lifecycle event be executed during the bootstrapping process. Defaults to false. This was added as an experimental additional flag to help with applications where they have critical disposal logic being executed within the componentWillUnmount lifecycle event.
  • context (Object, optional)

    Any context you wish to expose to your application. This will become available to the entire application and could be useful for exposing configuration to your bootstrap methods.

    e.g. { myContextItem: 'foo' }

Returns

A Promise that resolves when the bootstrapping has completed.

react-async-bootstrapper's People

Contributors

almmiko avatar birkir avatar ctrlplusb avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

react-async-bootstrapper's Issues

can not use dynamic import

when i use dynamic import in react router as follows, the following problem has occurred
the error only occurred in development mode, not in production mode

import React from 'react'
import { Route, Redirect, withRouter, } from 'react-router-dom'
import Loadable from 'react-loadable'

import CircularProgress from '@material-ui/core/CircularProgress'
// right

// import TopicList from 'views/topic-list'
// import TopicDetail from 'views/topic-detail'
// import TopicCreate from 'views/topic-create'
// import UserLogin from 'views/user/login'
// import UserInfo from 'views/user/info'

// wrong
const TopicList = Loadable({
  loader: () => import('views/topic-list'),
  loading() {
    return <CircularProgress />
  },
})

const TopicDetail = Loadable({
  loader: () => import('views/topic-detail'),
  loading() {
    return <CircularProgress />
  },
})

const TopicCreate = Loadable({
  loader: () => import('views/topic-create'),
  loading() {
    return <CircularProgress />
  },
})

const UserLogin = Loadable({
  loader: () => import('views/user/login'),
  loading() {
    return <CircularProgress />
  },
})

const UserInfo = Loadable({
  loader: () => import('views/user/info'),
  loading() {
    return <CircularProgress />
  },
})


export default () => [
  <Route exact render={() => <Redirect to="/list" />} path="/" key="home" />,
  <Route component={TopicList} path="/list" key="list" />,
  <Route component={TopicDetail} path="/detail/:id" key="detail" />,
]
{ Error: Cannot find module './0.server-entry.js'
    at Function.Module._resolveFilename (module.js:548:15)
    at Function.Module._load (module.js:475:25)
    at Module.require (module.js:597:17)
    at require (internal/module.js:11:18)
    at Function.requireEnsure [as e] (server-entry.js:46:25)
    at loader (webpack:///./client/router/index.jsx?:69:60)
    at load (/Users/ender/learning/github/react-cnode/node_modules/react-loadable/lib/index.js:28:17)
    at init (/Users/ender/learning/github/react-cnode/node_modules/react-loadable/lib/index.js:121:13)
    at new LoadableComponent (/Users/ender/learning/github/react-cnode/node_modules/react-loadable/lib/index.js:150:7)
    at /Users/ender/learning/github/react-cnode/node_modules/react-tree-walker/dist/react-tree-walker.js:174:30 code: 'MODULE_NOT_FOUND' }

Bootstrap not passing proper context on SSR

With the latest react version I'm unable to make this library work, for some reason react-tree-walker is not able to keep the context of the provider.

I was able to reproduce the error inside the asyncBootstrapper.test.js:

`
import React, { Component } from 'react'
import asyncBootstrapper from '../'

const ThemeContext = React.createContext('light')

describe('asyncBootstrapper()', () => {
let values = []
let actualContext
let contextValue

class DeprecatedAPI extends Component {
asyncBootstrap() {
values.push(this.props.id)
return true
}

render() {
  return <div>{this.props.children}</div>
}

}

class NewAPI extends Component {
bootstrap() {
values.push(this.props.id)
actualContext = this.context
return true
}

render() {
  return <div>{this.props.children}</div>
}

}

const app = Foo => (
<ThemeContext.Provider value='dark'>


Test





<ThemeContext.Consumer>
{value => {
contextValue = value
return value
}}
</ThemeContext.Consumer>



</ThemeContext.Provider>
)

beforeEach(() => {
values = []
})

it('deprecated API', () =>
asyncBootstrapper(app(DeprecatedAPI)).then(() =>
expect(values).toEqual([1, 2, 4, 3]),
))

it('new API', () =>
asyncBootstrapper(app(NewAPI), null, { bespokeContext: true }).then(() => {
expect(values).toEqual([1, 2, 4, 3])
expect(actualContext).toEqual({
bespokeContext: true,
reactAsyncBootstrapperRunning: true,
})
expect(contextValue).toEqual('dark')
}))
})
`

Updating the react version to 16.6.3 you should be able to reproduce the error.

When using this library, use axios to make requests in the bootstrap() method of the component. If the url is a full full path, you can request it. If you use node to forward, you can't ask.

This is my code:
TopicDetail.jsx
class TopicDetail extends React.Component{ constructor(){ super(); this.state={ newReply:'' } } componentDidMount(){ // const id = this._getId(); // this.props.topicStore.getTopicDetail(id); } bootstrap(){ const id = this._getId(); return this.props.topicStore.getTopicDetail(id).then(() => { return true }).catch(() => { return false }); }}
The method of request, written in mobx:
@action fetchTopics(tab){ //console.log('tab',tab) return new Promise((resolve,reject) => { // console.log('syncing', this.syncing) this.syncing = true this.topics= [] get('/api/topics',{ mdrender:false, tab:tab }).then(resp => { // console.log('resp',resp) if(resp.success){ resp.data.forEach(topic => { // console.log('topic',topic) this.addTopic(topic) }) resolve() }else{ reject() } }).catch(err => { reject(err) this.syncing = false }) }) }

the get method
export const get = (url,param) => { //console.log('url',url) let params = param || {} return new Promise ((resolve,reject) => { console.log('axios') axios({ method:'get', url, params, }).then((resp) => { console.log('resp1', resp) const data = resp.data; if(data && data.success === true){ resolve(data) }else{ reject(data) } }).catch((err) => { if(err.response){ reject(err.response.data) }else{ reject({ sucess:false, err_msg:err.message }) } }) }) }

server.js
app.use('/api/user', require('./util/handle-login')) app.use('/api', require('./util/proxy'))

I found that I did not intercept 'api' at all.

This problem has been solved for a long time and I hope to get your help.
Thank you!

Cannot read property 'dense' of undefined in development env

I use + react-async-bootstrapper for react ssr development, it will report an error in the development environment, the production environment is normal, how should this problem be solved, thank you

"TypeError: Cannot read property 'dense' of undefined\n   
 at Object.children (server-entry.js:29:31469)\n    
at recursive (/Users/ender/learning/github/react-cnode/node_modules/react-tree-walker/dist/react-tree-walker.js:129:41)\n   
 at /Users/ender/learning/github/react-cnode/node_modules/react-tree-walker/dist/react-tree-walker.js:153:26\n   
 at <anonymous>\n   
 at process._tickCallback (internal/process/next_tick.js:189:7)"

The application calls render twice using react-async-bootstrapper

HI, i'm trying to upgrade my universal application from react-router 3 to 4, and the only almost functioning async component its yours (I've tried react-loadable, but it flickers on first render...).

But I've noticed that calling asyncBootstrapper(content).then(...), trigger the rendering twice (4 times if we consider the ssr).

There is no solution to that problem? I've made something wrong?

My project experiment: https://github.com/cloud-walker/rr4-ssr-code-split-experiment

SSR does not work correctly after upgrading to 2.0.0

Before upgrading, it will wait for API called even at the first time and render whole page, but after upgrading, no list content is renderer at the first time.

You can check https://github.com/JounQin/react-hackernews and run yarn dev and visit http://localhost:4000 to view the correct SSR page, then simply upgrade react-async-bootstrapper to 2.0.0, then you will see incorrect page at first rendering.

Online Link: https://react-hn.now.sh/ (not upgrade yet)

I don't know if it is related to [email protected] or [email protected] is not compatible with [email protected]

in material-ui v1.0 has some wrong

const serialize = require("serialize-javascript");
const ejs = require('ejs');

const ReactDOMServer = require("react-dom/server");
const bootstrapper = require('react-async-bootstrapper')
const Helmet = require('react-helmet').default
const SheetsRegistry = require('react-jss').SheetsRegistry
const createGenerateClassName = require('@material-ui/core/styles').createGenerateClassName
const createMuiTheme = require('@material-ui/core/styles').createMuiTheme
const themeColors = require('../../client/themes/theme-default')

const getStoreState = (stores) => {
  return Object.keys(stores).reduce((result, storeName) => {
    result[storeName] = stores[storeName].toJson()
    return result;
  }, {})
}

module.exports = (bundle, template, req, res) => {
  const user = req.session.user;
  const createStoreMap = bundle.createStoreMap
  const createApp = bundle.default
  const routerContext = {};
  const stores = createStoreMap();
  const sheetsRegistry = new SheetsRegistry();
  // Create a theme instance.
  const theme = createMuiTheme(themeColors);
  const generateClassName = createGenerateClassName();

  if (user) {
    stores.appState.user.isLogin = true;
    stores.appState.user.info = user;
  }

  const app = createApp(stores, routerContext, sheetsRegistry, generateClassName, theme, req.url);
  return new Promise((resolve, reject) => {
    bootstrapper(app).then(() => {
      console.log('==json==================');
      console.log(JSON.stringify(routerContext, null, '\t'));
      console.log('========================');
      if (routerContext.url) {
        res.status(302).setHeader('Location', routerContext.url)
        res.end()
        return
      }
      const helmet = Helmet.rewind();
      const state = getStoreState(stores);
      const content = ReactDOMServer.renderToString(app);
      const html = ejs.render(template, {
        appString: content,
        initialState: serialize(state),
        meta: helmet.meta.toString(),
        title: helmet.title.toString(),
        style: helmet.style.toString(),
        link: helmet.link.toString(),
        materialCss: sheetsRegistry.toString(),
      })
      res.send(html);
      resolve()
    }).catch(reject)
  })
}

it will be rerender

example with react-router-dom

As react router dom is the most used router for react. it will be nice to have an example of integrating the router with the bootstrapper to make sure the bootstrap function is called on route change (somehow)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.