Skip to content

New context mixin concept for react, redux, graphql, simplified templates

Compare
Choose a tag to compare
@ZauberNerd ZauberNerd released this 23 Nov 13:38
· 2441 commits to master since this release

8.0.0 (2017-11-22)

This update changes the previous context implementation and fixes a couple of other issue (read more about those below).

BREAKING CHANGES

  • renderer: createRenderer signature changed to options hash instead of discrete args
    Before: The default export of hops-renderer (createRenderer) accepted two options:

    function createRenderer(webpackConfig, watchOptions) {...}

    Now it only accepts one argument, that is an options object with the following shape:

    var options = { webpackConfig, watchOptions, hopsConfig };
  • react: new context mixin mechanism using mixinable

  • redux: new context mixin mechanism using mixinable

  • graphql: new context mixin mechanism using mixinable
    Before: render from hops-react could be called with either just the App or the App and an options object:

    import { render } from 'hops-react';
    render(<App />)

    or

    import { render } from 'hops-react';
    render(<App />, { mountpoint: '#foo' })

    After: render always needs to be called with the App (and a hops context):

    import { render, createContext } from 'hops-react';
    render(<App />, createContext({ mountpoint: '#foo' }))

    the createContext method accepts an options object and can be imported as a named import from hops-react.

    import { render, createContext } from 'hops-react';

    Also: There no longer exists a class-based Context, but instead the context is composed in a
    functional manner, via createContext, contextDefinition and combineContexts (where
    createContext is just a shorthand for combineContexts(contextDefinition)).

    The context methods are now implemented in the contextDefinition.
    And multiple context definitions (for example: react, redux and graphql)
    can be combined to a context creator function (which will return a combined context, that can be used as second argument to render).

    Most packages also export a convenience createContext method, which is already a combination of the react context and their owhn context.
    For example: hpos-redux combines hops-react and hops-redux:

    import { render } from 'hops-react';
    import { createContext } from 'hops-redux';
    
    render(<App />, createContext({ reducers: {...} }));

    And hops-graphql combines hops-react and hops-graphql:

    import { render } from 'hops-react';
    import { createContext } from 'hops-graphql';
    
    render(<App />, createContext({ graphql: { cache, link } }));

    If you want to have a context that combines all three of them, you will need to create it yourself:

    import { render, combineContexts, contextDefinition as reactContext } from 'hops-react';
    import { contextDefinition as reduxContext } from 'hops-redux';
    import { contextDefinition as graphqlContext } from 'hops-graphql';
    
    import App from './app';
    
    const createContext = combineContexts(reactContext, reduxContext, graphqlContext);
    
    export default render(<App />, createContext({ mountpoint: '#foo', reducers: {...}, graphql: {...} }));

    Additional information for extending / overwriting context definitions:
    As the context implementation has changed (instead of using a class based approach
    we have switched to mixinable) which favors functional
    composition and enables us to use multiple contexts at the same time.
    The hops lifecycle definitions are as follows:
    DOM

    exports.combineContexts = mixinable({
      bootstrap: mixinable.parallel,
      enhanceElement: mixinable.pipe,
      getMountpoint: mixinable.override
    });

    Node

    exports.combineContexts = mixinable({
      bootstrap: mixinable.parallel,
      enhanceElement: mixinable.pipe,
      getTemplateData: mixinable.pipe,
      renderTemplate: mixinable.override
    });

    This allows us to implement these methods in contextDefinitions and combine multiple
    contextDefinitions via combineContexts(contextDefinition0, contextDefinition1).
    This means, that all bootstrap methods in all combined contexts will be executed in parallel
    and all enhanceElement methods will be executed one after another and their return value will be the the input argument to the next enhanceElement implementation, allowing you to compose (pipe) them.
    mixinable.override will only execute the respective method of the last implementation passed into combineContexts.

    To get a better understanding of how the context composition works, we suggest, that you take a look at the following files:

  • build-config: remove support for turning flow types to prop types
    Hops now no longer has built-in support for converting flow type
    annotations into react prop-type definitions.
    It still supports flow out of the box, via the babel-react preset,
    but now it only removes flow type annotations from the build instead
    of converting them to prop-types.

  • template-react: The template hops-template-react now no longer contains flow type annotations.

  • template-react: remove graphql example code
    hops-template-react (default template) now no longer supports graphql
    out of the box. You can add the support yourself again or use a
    different template (we will provide a new hops-template-graphql shortly).

  • plugin: Constructor signature changed to options hash instead of discrete args.
    Before: The webpack plugin hops-plugin used to be instantiated with three discrete arguments:

    new HopsPlugin(locations, webpackConfig, watchOptions);

    Now it accepts only a single options object:

    var options = { locations, webpackConfig, watchOptions, hopsConfig };
    new HopsPlugin(options);

Bug Fixes

  • build-config: exclude absolute paths from bundled config (4ecc41f)
  • build-config: inline core-js polyfills in bundled Node.js code (37e0feb)
  • build-config: replace babel-minify with uglify-es (f1be32c)
  • local-cli: if _gitignore exists, rename it to .gitignore (520a6da)
  • react: make sure to only hydrate on first pass (561cb89)
  • template-minimal: keep gitignore after publish by renaming it (d9e7e2d)
  • template-react: add missing prop-types dependency (5fb80f2)
  • template-react: keep gitignore after publish by renaming it (afb28ae)

Code Refactoring

  • build-config: remove propTypes in production builds (50c9d6c)
  • graphql: make main export a context mixin (d097d2d)
  • graphql: rename mixin definition export (dfd1d4b)
  • plugin: switch to options hash (64e0f24)
  • redux: make main export a context mixin (f5edae6)
  • redux: rename mixin definition export (321e733)
  • template-react: remove flow type annotations (4f7bba9)
  • template-react: remove graphql from default template (4533445)

Features

  • config: allow targeting specific Node version in babel preset (4437c6b)
  • graphql: implement simplified mixin support (509c1b5)
  • graphql: introduce mixin support (813196f)
  • hops-build-config: add source maps to production build output (9cfde51)
  • hops-build-config: add webpack-stats-plugin to build (a752635)
  • react: add combineContexts, refactor exports (8bd2955)
  • react: implement simplified mixin support (6f8bf5c)
  • react: introduce mixin support (3a575b1)
  • redux: implement simplified mixin support (dfed624)
  • redux: introduce mixin support (c0da538)
  • renderer: add support for an options hash (8206ad1)
  • renderer: make renderer use bootstrapServer config (ce2298d)

Performance Improvements

  • graphql: remove fs.existsSync() check from context (1441d20)