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

Where is context set? #5

Closed
dsernst opened this issue Jan 14, 2016 · 3 comments
Closed

Where is context set? #5

dsernst opened this issue Jan 14, 2016 · 3 comments

Comments

@dsernst
Copy link
Contributor

dsernst commented Jan 14, 2016

I'm trying to add this to server-side render a particular component with its style.

I've installed this package and prepended it to the list of loaders for .scss:

  module: {
    loaders: [
      JS_LOADER,
      ...config.module.loaders,
      {
        test: /\.scss$/,
-        loaders: ['css-loader', 'postcss-loader'],
+        loaders: ['isomorphic-style-loader', 'css-loader', 'postcss-loader'],
      },
    ],
  },

I've added all the code described in the readme for stateless react components:

import React, { PropTypes } from 'react'
import style from './About.scss'

function About(props, context) {
  console.log('context:', context)
  context.insertCss(style)
  return (
    <div className="About">
      ...
    </div>
  )
}

About.contextTypes = { insertCss: PropTypes.func.isRequired }

export default About

But when I try to build this on the server (using npm start from react-static-boilerplate) these errors are thrown:

Warning: Failed Context Types: Required context `insertCss` was not specified in `About`. Check the render method of `_default`.
context: { insertCss: undefined }
TypeError: context.insertCss is not a function
    at new About (/Users/dsernst/Documents/project/build/app.node.js:1401:13)

Any idea why context.insertCss is undefined? Is there a step I'm missing? Was I supposed to invoke getChildContext somewhere up the tree?

@dsernst dsernst changed the title Where is context getting passed from? Where is context set? Jan 14, 2016
@koistya
Copy link
Member

koistya commented Jan 14, 2016

isomorphic-style-loader adds ._insertCss() and ._getCss() helper functions to the style object. Check this out:

MyComponent.scss
.root { color: red; }
MyComponent.js
import React from 'react';
import s from './MyComponent.scss';

function MyComponent() {
  s._insertCss();                                     // <-- injects CSS into the DOM
  return <pre className={s.root}>{s._getCss()}</pre>; // <-- renders CSS as a string
}

export default MyComponent;

In order to make the component above isomorphic (being able to extract used by it CSS during pre-render), you need to create a custom insertCss(styles) function and pass it to your top-level react component via context. Your top-level react component (e.g. <Layout />) needs to have getChildContext() { } method that sets custom insertCss function, which might be as simple as (styles) => styles._insertCss() for client-side code and (styles) => { css.push(styles._getCss(); } for server-side code.

Here is a helper component that calls context.insertCss(style) before component is mounted, and removes previously injected CSS before component is unmounted:

withStyles.js
import React, { Component, PropTypes } from 'react';

function withStyles(BaseComponent, ...styles) {
  return class StyledComponent extends Component {
    static contextTypes = {
      insertCss: PropTypes.func.isRequired,
    };

    componentWillMount() {
      this.removeCss = this.context.insertCss.apply(undefined, styles);
    }

    componentWillUnmount() {
      this.removeCss();
    }

    render() {
      return <BaseComponent {...this.props} />;
    }
  };
}

export default withStyles;

Usage sample:

import React, { PropTypes } from 'react';
import withStyles from '../withStyles.js';
import s from './About.scss';

function AboutPage() {        // <- no need to explicitly call s._insertCss()
  return (
    <div className={s.root}>
      ...
    </div>
  );
}

export default withStyles(AboutPage, s);

@dsernst
Copy link
Contributor Author

dsernst commented Jan 14, 2016

Ok, it seems a bit more complicated for react-static-boilerplate, because there's no css to push into for the context.

I've gotten it mostly working, by updating:

  • app.js
  • components/Html/Html.js
  • components/Layout/Layout.js
  • components/About/About.js (e.g. MyComponent.js, for testing)
  • package.json
  • tools/render.js
  • tools/webpack.config.js

But one aspect that still doesn't work right now is HMR, nor am I setting a different context on the server vs client. Since this question is now answered. I'll close this Issue and reopen as a PR over in that project.

Thanks for your help.

@mqliutie
Copy link

@koistya
I'm sorry I think I did not do it correctly. There is my code

class Todos extends React.Component {

    constructor ({staticContext}) {
        super();
        // if (AppUtil.isClient()) {
        //     style._insertCss();
        // } else {
        //     staticContext.insertCss(style);
        // }
    }

    getChildContext () {
        return {
            insertCss: (styles) => styles._insertCss()
        }
    }

    async componentDidMount () {
        const { todo = {}, dispatch } = this.props;
        const { todoList } = todo;
        if (todoList.length <= 0) {
            dispatch(getTodoList());
        }
    }

    render () {
        const { todo } = this.props;
        const { todoList } = todo;
        return <ul className={style.test}>{todoList.map(todo => <li key={todo.id}>{todo.title}</li>)}</ul>;
    }
}

export default withStyles(style)(Todos);

The error was shown Uncaught TypeError: Cannot read property 'apply' of undefined

this.removeCss = (_context = this.context).insertCss.apply(_context, styles);

isomorphic-fetch version is 2.2.1

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

No branches or pull requests

3 participants