Skip to content

A new approach of isomorphic css resolution with webpack, css-loader(css-modules) powered by redux.

License

Notifications You must be signed in to change notification settings

openameba/styledux

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

45 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

styledux

A new approach of isomorphic css resolution with webpack, css-loader(css-modules) powered by redux.

IT'S style-dux, not styled-ux.

Status: beta

Features

  • Plain, simple, original css with css modules (with Webpack css-loader). Also support LESS, SCSS, Sass, Stylus, PostCSS...
  • NO extra editor plugin required, because it's only PLAIN css + jsx.
  • Universal javascript support (Server side rendering + Client side rendering)
  • Thread-safe server side rendering, renderToNodeStream() / renderToStaticNodeStream() is OK.
  • Remove styles when render() returns null automatically
  • Middleware support (Powered by redux)
  • Webpack Hot Module Replacement (HMR)

Installation

For npm

npm install styledux

For yarn

yarn add styledux

Usage

1. Configuration

Add styledux/loader before css-loader to your webpack configuration.

module: {
  rules: [
    {
      test: /\.css$/,
      use: [
        {
          loader: 'styledux/loader'
        },
        {
          loader: 'css-loader',
          options: {
            modules: true, // css modules support, important!!
            importLoaders: 1,
            localIdentName: process.env.NODE_ENV === 'production' ? '_[hash:base64:8]' : '[name]__[local]___[hash:base64:5]',
            minimize: process.env.NODE_ENV === 'production',
            sourceMap: true,
          }
        },
        {
          loader: 'postcss-loader'
        }
      ]
    }
  ]
}

styledux/loader is simply to export locals that generated from css-loader and wrap original css contents with a function _().

2. Decorate React components with @withStyle

ExampleComponent.css

.Block {
  background-color: green;
  display: block;
  left: 0;
  opacity: 0;
  position: fixed;
  width: 200px;
  height: 200px;
}

.SubModule {
  background-color: blue;
  display: block;
  width: 20px;
  height: 20px;
}

ExampleComponent.js

import { withStyle } from 'styledux'

import style from './ExampleComponent.css';

@withStyle(style)
export class ExampleComponent extends React.Component {
    render() {
      return (
        <div className={style.Block}>
          <div className={style.SubModule} />
        </div>
      );
    }
}

For stateless component

ExampleStatelessComponent.js

import { withStyle } from 'styledux'

import style from './ExampleComponent.css';

function ExampleStatelessComponent() {
  return (
    <div className={style.Block}>
      <div className={style.SubModule} />
    </div>
  );
}
const component = withStyle(style)(ExampleStatelessComponent);
export default component;

3. Server side rendering

import ReactDOMServer from 'react-dom/server';
import { createStyleduxStore, StyleduxProvider, mapStateOnServer } from 'styledux';

export function renderHTML(App) {
  const styleStore = createStyleduxStore();
  const appHtml = ReactDOMServer.renderToString(
    <StyleduxProvider store={styleStore}>
      <App />
    </StyleduxProvider>
  );
  const styles = mapStateOnServer(styleStore);
  return [
    '<!doctype html><html><head>',
    ...styles,
    '<style id="main_css"></style>', // for options.insertInto on client side
    // You can also add other styles which have higher priority
    '</head><body><div id="app">',
    appHtml,
    '</div></body></html>',
  ].join('');
}

4. Client Side Rendering

import ReactDOM from 'react-dom';
import { createStyleduxStore, StyleduxProvider, handleStateChangeOnClient } from 'styledux';

function rehydrateApp(App) {
  ReactDOM.hydrate(
    <StyleduxProvider store={createStyleduxStore(handleStateChangeOnClient({ insertAt: '#main_css' }))}>
      <App />
    </StyleduxProvider>,
    document.getElementById('app')
  );
}

API

<StyleduxProvider store>

See https://github.com/reactjs/react-redux/blob/master/docs/api.md#provider-store

Props
  • store: Plain redux store that created with createStyleduxStore()
  • children
Example:
import { StyleduxProvider } from 'styledux';

ReactDOM.render(
  <StyleduxProvider store={styleStore}>
    {your_app_component}
  </StyleduxProvider>,
  rootElement
)

createStyleduxStore(...middlewares)

Arguments
  • middlewares: Plain redux middlewares.

You can create your own middlewares to handle state changes.

handleStateChangeOnClient(options) creates a middleware that mount styles like style-loader.

Example

On server:

import { createStyleduxStore } from 'styledux';

const styleStore = createStyleduxStore();

On client:

import { createStyleduxStore, handleStateChangeOnClient } from 'styledux';

const middleware = handleStateChangeOnClient({ insertAt: '#main_css' });
const styleStore = createStyleduxStore(middleware);

withStyle(style)

Decorate a react component with style. It monitors render() of react component.

It dispatches an ADD_STYLE action when render() returned non-empty result, and dispatches a REMOVE_STYLE action when render() returned null.

Arguments
  • style (Object | Array): style object that was generated by styledux/loader.
Example
import { withStyle } from 'styledux';

import style1 from './style1.css';
import style2 from './style2.css';

@withStyle([style1, style2])
class ExampleComponent extends React.Component {
}
import { withStyle } from 'styledux';

import style from './style.css';

@withStyle(style)
class ExampleComponent extends React.Component {
}

mapStateOnServer(options) and handleStateChangeOnClient(options)

The built-in adapter for styledux.

When you build a universal web application, make sure mapStateOnServer(options) and handleStateChangeOnClient(options) use the same options.

options:

  • attrs (Object): Add custom attrs to <style></style> (default: {}).
  • transform (Function): Transform/Conditionally load CSS by passing a transform/condition function. (default: (obj) => obj)
  • transformId (Function): Transform webpack module id to element id. (default: see getOptions )
  • insertAt (null|String): Inserts <style></style> at the given position. Different from style-loader. (default: null)
  • insertInto (String): Inserts <style></style> into the given position. (default: )

mapStateOnServer(styleduxStore, options)

Only required for server-side rendering. Generate <style> from styleduxStore on server side.

Example
const styleStore = createStyleduxStore();
const content = ReactDOMServer.renderToString(
  <StyleduxProvider store={styleStore}>
    <App />
  </StyleduxProvider>
);
const styles = mapStateOnServer(styleStore);

const html = [
  '<!doctype html><html><head>',
  ...styles,
  '<style id="main_css"></style>', // The position for insert_at
  '<link rel="stylesheet" type="text/css" href="custom_theme.css">', // Other styles that may have higher priority
  '<script src="main.js" defer></script>',
  '</head><body><div id="app">',
  content,
  '</div></body></html>'
].join('');

handleStateChangeOnClient(options)

Handling state changes and mount styles dynamically on client side.

Example
const styleStore = createStyleduxStore(handleStateChangeOnClient({
  insertAt: '#main_css'
}));

const app = (
  <StyleduxProvider store={styleStore}>
    <App />
  </StyleduxProvider>
);
ReactDOM.hydrate(app, document.getElementById('app'));

LICENSE

MIT

About

A new approach of isomorphic css resolution with webpack, css-loader(css-modules) powered by redux.

Resources

License

Stars

Watchers

Forks

Packages

No packages published