Skip to content
forked from reduxjs/redux

An experiment in fully hot-reloadable Flux

License

Notifications You must be signed in to change notification settings

bradleyboy/redux

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

63 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

redux

An experiment in fully hot-reloadable Flux.

The API might change any day.
Don't use in production.

Why another Flux framework?

Read The Evolution of Flux Frameworks for some context.

Design Goals

  • Hot reloading of everything.
  • A hook for the future devtools to "commit" a state, and replay actions on top of it during hot reload.
  • No createAction, createStores, wrapThisStuff. Your stuff is your stuff.
  • I don't mind action constants. Seriously.
  • Keep Flux lingo. No cursors or observables in core.
  • Have I mentioned hot reloading yet?

Demo

git clone https://github.com/gaearon/redux.git redux
cd redux
npm install
npm start

What's it look like?

Actions

// Still using constants...
import {
  INCREMENT_COUNTER,
  DECREMENT_COUNTER
} from '../constants/ActionTypes';

// But action creators are pure functions returning actions
export function increment() {
  return {
    type: INCREMENT_COUNTER
  };
}

export function decrement() {
  return {
    type: DECREMENT_COUNTER
  };
}

// Can also be async if you return a function
// (wow, much functions, so injectable :doge:)
export function incrementAsync() {
  return dispatch => {
    setTimeout(() => {
      dispatch(increment());
    }, 1000);
  };
}

// Could also look into state in the callback form
export function incrementIfOdd() {
  return (dispatch, state) => {
    if (state.counterStore.counter % 2 === 0) {
      return;
    }

    dispatch(increment());
  };
}

Stores

// ... too, use constants
import {
  INCREMENT_COUNTER,
  DECREMENT_COUNTER
} from '../constants/ActionTypes';

// but you can write this part anyhow you like:

const initialState = { counter: 0 };

function increment({ counter }) {
  return { counter: counter + 1 };
}

function decrement({ counter }) {
  return { counter: counter - 1 };
}

// what's important is that Store is a pure function too
export default function counterStore(state = initialState, action) {
  // that returns the new state when an action comes
  switch (action.type) {
  case INCREMENT_COUNTER:
    return increment(state, action);
  case DECREMENT_COUNTER:
    return decrement(state, action);
  default:
    return state;
  }
}

// bonus: no special support needed for ImmutableJS,
// just return its objects as the state.

Components

Dumb Components

// The dumb component receives everything using props:
import React, { PropTypes } from 'react';

export default class Counter {
  static propTypes = {
    counter: PropTypes.number.isRequired,
    increment: PropTypes.func.isRequired,
    decrement: PropTypes.func.isRequired
  };

  render() {
    const { counter } = this.props;
    return (
      <p>
        Clicked: {counter} times
      </p>
    );
  }
}

Smart Components

// The smart component may inject actions
// and observe stores using <Container />:

import React, { Component } from 'react';
import { Root, Container } from 'redux';
import { increment, decrement } from './actions/CounterActions';
import counterStore from './stores/counterStore';
import Counter from './Counter';

export default class CounterContainer {
  render() {
    // stores must be an array.
    // actions must be a string -> function map.
    // props passed to children will combine these actions and state.
    return (
      <Container stores={[counterStore]}
                 actions={{ increment, decrement }}>
        {props => <Counter {...props} />}
      </Container>
    );
  }
}

Decorators

Don't want to separate dumb and smart components just yet? Use the decorators!
They work exactly the same as the container components, but are lowercase:

import React, { PropTypes } from 'react';
import { increment, decrement } from './actions/CounterActions';
import { container } from 'redux';
import counterStore from './stores/counterStore';

@container({
  actions: { increment, decrement },
  stores: [counterStore]
})
export default class Counter {
  static propTypes = {
    increment: PropTypes.func.isRequired,
    decrement: PropTypes.func.isRequired,
    counter: PropTypes.number.isRequired
  };

  render() {
    return (
      <p>
        Clicked: {this.props.counter} times
        {' '}
        <button onClick={() => this.props.increment()}>+</button>
        {' '}
        <button onClick={() => this.props.decrement()}>-</button>
      </p>
    );
  }
}

The root component

import React from 'react';
import { root } from 'redux';
import * from './stores/index';

// Let it know about all the stores
@root(stores)
export default class App {
  /* ... */
}

FAQ

How does hot reloading work?

Can I use this in production?

I wouldn't. Many use cases are not be considered yet. If you find some use cases this lib can't handle yet, please file an issue.

About

An experiment in fully hot-reloadable Flux

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • JavaScript 98.3%
  • HTML 1.2%
  • Shell 0.5%