Giter Site home page Giter Site logo

redux's People

Contributors

aaronpowell96 avatar acdlite avatar aikoven avatar andarist avatar andrewmcgivery avatar aryaemami59 avatar cellog avatar chentsulin avatar chrisjamesc avatar coryhouse avatar dbartholomae avatar ellbee avatar emmenko avatar eskimojo14 avatar frankychung avatar gaearon avatar hedgerh avatar mahendrabishnoi29 avatar markerikson avatar maxhallinan avatar methuselah96 avatar mikekidder avatar mindjuice avatar mxstbr avatar nickmccurdy avatar omnidan avatar phryneas avatar slorber avatar timdorr avatar vramana 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  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

redux's Issues

Bring back HOCs on top of the container components

As per #5 (comment), let's bring back HOCs, but this time built on top of the container components. Container components are lower level.

This work would include:

  • Change root to be a container component called Root
  • Implement root and container as HOCs that wrap the components
  • I want to keep using root HOC + Container component in the examples
  • Tricky file naming. I guess let's put them into addons folder but keep root etc as the export name.

@hugobessaa

[QUESTION] 3-phase async dispatch

In Flummox we had the ability to auto-dispatch begin, success and failure actions. I assume in redux we can do this by creating a custom middleware, which could inspect the type of the action which returned the promise and then dispatched a success or failure action depending on whether the promise gets resolved or rejected. Does this sound correct to you? If so, would it make sense to include something like this as stock middleware in redux?

Proposal: make exported store name insignificant

When specifying Container or @container, the name under which the store is exported is insignificant. You just assign it a string key:

@container({
  stores: { counter: counterStore }
})

and it appears as counter.

There is now a single place where it's still significant: the action creator callback form.

export function incrementIfOdd() {
  return (dispatch, state) => {
    if (state.counterStore % 2 === 0) { // reading from counterStore state here
      return;
    }

    dispatch(increment());
  };
}

It's not obvious that it's called counterStore because of export { default as counterStore } from './counterStore inside stores/index.js passed to the @root.

An alternative is to let state be ES6 map with Store functions as keys. Then you'd do

import counterStore from './stores/counterStore';

export function incrementIfOdd() {
  return (dispatch, state) => {
    if (state.get(counterStore) % 2 === 0) { // get state for the store
      return;
    }

    dispatch(increment());
  };
}

This code won't break if you rename the export, and also makes the dependency clear.
Thoughts?

Instead of providing HOCs, provide container components

As in the docs, the decorator @observes could couple the counter with the counterStore, instead of giving it the freedom from receiving the counter from any data source via props.

I like flummox's FluxComponent way of doing stuff: your component is pure and a top-level component chooses how to feed it.

import { Observes } from 'redux/components';

<Observes store="counterStore">
  <Counter /* counter={counterStore.counter} */ />
</Observes>

For the @performs decorator we could have an equivalent component that wires an event of a component (e.g. onClick, onIncrement) with an action.

import { Performs } from 'redux/components';
import increment from './actions/CounterActions.js';

<Performs actions={{'onClick': increment}}>
  <IncrementButton />
</Performs>

I think we have much more power using components this way. More testable and reusable.

I'm far from being an expert on decorators and even on Flux, so please show your views and let's discuss this thread πŸ˜„

Suggestion: Possible changes

  1. In Counter.js, fat arrow functions; lexical bind to 'this' not needed in this case. Still demonstrated in Todo example.
<button onClick={() => increment()}>+</button>

to

<button onClick={increment}>+</button> 
  1. On the Counter example, change the imports for ActionTypes:
import {
  INCREMENT_COUNTER,
  DECREMENT_COUNTER
} from '../constants/ActionTypes';

to

import * as types from  '../constants/ActionTypes';

and then use accordingly in case statements, etc..

 types.INCREMENT_COUNTER

What advantages over competition?

I read your flux evolution blog post (very good stuff) and have been looking at the other flux implementations, so far microcosm is looking the best to me. I still have to do research on some of the others you listed but after looking at this projects README I'm not sure I understand what this provides that the other flux implementations you mention don't provide. Could you add a section to the README explaining?

Proposal: change custom dispatcher signature

As rightly noted by @vslinko, it's better to provide getState instead of initialState.

I think I was wrong now when I said this. In fact, I find it now easier to implement composable dispatchers only if each β€œparent” has the ability to β€œintercept” both getState and setState, and if the child can β€œquery” the latest state.

I'm going to keep this open for a while, as I'm still not 100% sure which approach is more correct, but I'm leaning towards (getState, setState) => action => () now as the signature for the dispatcher.

Tests

Tests are sorely missing. Gonna try to fill that gap today.

Implement devtool hooks

When #6 is done, I want to use it to implement hooks for debugging and time travelling from console.

The API could be something like

ReduxDebug.printLastActions();
ReduxDebug.printLastStates();
ReduxDebug.jumpTo(stateId);

etc.

Why dehydration resets dispatcher?

export default async function renderPage(url) {
  const dispatcher = createDispatcher(composeStores(stores))

  const {title} = await dispatcher.perform(runTransition(url))

  // I need to get atom to make some checks
  const {atom} = dispatcher.dehydrate()

  if (!atom.routerStore.componentKey) {
    return {
      status: 404
    }
  }

  // But after that checks I should return atom into dispatcher for render usages
  // I want to remove this step
  dispatcher.hydrate({atom})

  const pageContent = React.renderToString(
    <Provider dispatcher={dispatcher}>
      {() => <Application dispatcher={dispatcher} />}
    </Provider>
  )

  return {
    body: appTemplate(title, pageContent, atom)
  }
}

Time travel

Reviving musings from #6, I'm going to post my progress on working on time travel & other devtool goodness here.

screen shot 2015-06-17 at 02 00 47

I have a working proof of concept of time travel that uses the default dispatcher and relies on store composition to keep a log of state. This is not feature-complete (only β€œjumping to state #N” is supported), but shows how to implement such functionality with Store composition:

export const StateKeys = {
  LOG: Symbol('Log')
};

export const ActionTypes = {
  JUMP_TO_STATE: Symbol('JUMP_TO_STATE')
};

export default function recorder(store) {
  return function Recorder(state = {}, action) {
    let { [StateKeys.LOG]: log = [], ...atom } = state;

    let nextAtom;
    switch (action.type) {
    case ActionTypes.JUMP_TO_STATE:
      const { index } = action;
      nextAtom = log[index].nextAtom;
      break;
    default:
      nextAtom = store(atom, action);
      break;
    }

    log = [...log, {
      action,
      atom,
      nextAtom
    }];

    return {
      [StateKeys.LOG]: log,
      ...nextAtom
    };
  }
}

Usage:

const store = composeStores(stores);
const dispatcher = createDispatcher(recorder(store)); // wrap the store
const redux = createRedux(dispatcher);

This relies on the root atom being β€œnormal” a JS object though. I think we'll need to store it internally as atom field instead inside a plain JS object created by us to support such β€œhidden” tool state.

I'd accept a PR that hides the user store's state inside an atom field on the Redux instance's state object, at the same time preserving the current API and behavior exactly. Redux.getState() should keep retuning that atom. Any other fields on the internal state will be assumed to be only used by the devtools.

Store function name is significant

A lot of problems were solved in 0.3 rewrite but unfortunately the Store function name is now significant. It is used as the key to the related state partition. Because Stores are not registered anywhere, there are a couple of weird problems:

  • Until some component references a Store, its data doesn't exist and it doesn't receive actions. Bad.
  • If you add another Store with the same function name they'll refer to the same data. Oops.

We can't rely on the function identity because of hot reloading. The same Store will he represented by different functions over time.

I feel this calls for a "Store registry" where you'd assign a unique key to each Store. I can't pinpoint the API yet so let's leave this open. If you have suggestions please let me know.

Ideal redux API

I have experiment about ideal redux API for me: https://github.com/vslinko/observable-components-test

  1. State should be a real observable. So I choice Rx as best observable implementation.
  2. wrapActionCreator(actionCreator)() replaced by performAction(actionCreator()).
  3. Pass performAction() to async action instead of dispatch(). Thats simplifies async action API: https://github.com/vslinko/observable-components-test/blob/master/src/actions/items.js#L25
  4. Flux shouldn't be dependent on React.

What are you think?

Allow configuration of Connector's prop-diffing algorithm

Right now Connector does a shallow comparison of its old and new props in shouldComponentUpdate but this isn't guaranteed to be correct if the props are mutable objects. Since the Connector's props aren't managed by Redux it might be right to let the user specify the prop differ or perhaps their own implementation of shouldComponentUpdate that gets composed with the default one.

Improve test coverage

Currently many branches are untested.
See for yourself:

git clone https://github.com/gaearon/redux.git
cd redux

npm install
npm run test:cov

Here's what I see:

=============================== Coverage summary ===============================
Statements   : 93.18% ( 328/352 )
Branches     : 51.72% ( 30/58 )
Functions    : 95.24% ( 60/63 )
Lines        : 93.16% ( 327/351 )
================================================================================

You can inspect particular untested branches by running

open coverage/lcov-report/index.html 

Here's an example of the behavior we should add tests for:

screen shot 2015-06-14 at 23 29 36

If you'd like to volunteer to help with adding the missing tests, please write in this issue.

Shape of props depends on number of stores

Changing the names of props based on number of stores subscribed to seems tricky.

Let's say I have a dumb component that takes a prop board ( { name: 'My Board' }), which should come from boardStore:

<Container stores={[boardStore]}
           actions={{ addList }}>
  {(props) => <Board {...props} /> }
</Container>

But because I only subscribed to one store, it gets flattened and the component only receives name prop.

If I need to add a subscription to another store userStore, the props change to:

{
  boardStore: { name: 'My Board' },
  userStore: ...,
  fooStore: ... // (This actually includes data from all stores, not just subscribed.)
}

I think it would be simplest, if the data would never be flattened. Then I can always just do:

<Container stores={[boardStore]}
           actions={{ addList }}>
  {(props) => <Board board={props.boardStore} /> }
</Container>

Another way could be to pass another function or a map of functions to map the state from stores to
props, but I think I actually prefer the former because of its simplicity.

Strategy for avoiding cascading renders

As I understand it, when you update a Store it broadcasts an update to all Connectors. Each Connector selects the slice of State that its interested in and re-renders itself by calling this.setState. React then recursively renders the component hierarchy until it hits a nested Connector, whose componentShouldUpdate method compares the old and new props (it also compares the Connector's State, which is guaranteed not to have changed because (a) it's a slice of a Store's immutable State and (b) this nested Connector hasn't called this.setState). If the props have changed, React continues rendering the component hierarchy. Once React is done, the second, nested Connector receives an update from the Store, selects its slice of State, and re-renders itself. In the worst-case scenario, a linear chain of n Connectors could result in O(n^2) render calls.

This is a small example that demonstrates this cascading render effect. Both Connectors are subscribed to the same slice of State, and the parent passes down a prop to the child.

type StoreState = {
  user: {
    name: string;
    age: number;
  };
};

@connect(state => ({ user: state.user }))
class Parent extends Component {
  render() {
    let { user } = this.state;
    let displayName = (user.age < 18) ? getFirstName(user.name) : user.name;
    return <Child displayName={displayName} />;
  }
}

@connect(state => ({ user: state.user }))
class Child extends Component {
  render() {
    let { user } = this.state;
    return (
      <ul>
        <li>{this.props.displayName}</li>
        <li>{this.props.age}</li>
        {__DEV__ && <li>this.state.name</li>}
      </ul>
    );
  }
}

This is what I'm currently thinking:

  1. The Connectors' subscriptions to the Redux instance need to be ordered such that a parent's subscription is before its children's. I believe this is already the case so we're good here.
  2. New: when a Connector is re-rendered, it selects its slice of State from the Stores and assigns it to this._state. This means a Connector always renders with the most up-to-date State even if it hasn't been notified of a change yet. It also means that Store updates are atomic from the perspective of the subscribing views... currently a Connector's props may be derived from the new State while the Connector's state is a slice of the old State.
  3. New: when the Connector is notified of a change, it selects its slice of State from the Store and compares it against this._state. If there's no change (e.g. because we already got an up-to-date slice when the parent re-rendered this Connector) then do nothing. If there is a change then we update this._state and call this.forceUpdate.

The main thing I'm going for is the atomic Store update from the perspective of the React components. From Redux's perspective, the updates are atomic in the sense that it doesn't notify subscribers till the Stores are done transforming the State. But from the perspective of a connected component it's receiving a mix of old and new State as props, while in traditional Flux the components always read a consistent view of the Stores.

Not working component update on state change.

Few things:
First one: Why this? https://github.com/gaearon/redux/blob/master/src/components/Connector.js#L19 - not sure whether "flux" lib should cream about this part.
Second one: due to custom method shouldComponentUpdate "rerendering" of component does not work.

Look at my code:
Inside container component:

<Connector select={select}>
              {({ userFooter, dispatcher }) =>

                <UserFooter userFooter={userFooter} {...bindActions(UserActions, dispatcher)} />
              }
            </Connector>

select method:

let select = (state) => {
  return {
    userFooter: state.userFooter
  }
}

Store:

const initialState = {
  isAuthenticated: false,
  email: ''
}

let setAuthenticated = (state) => {

  state.isAuthenticated = false
  state.email = ''

  return state
}

export default createStore(initialState, {

  [LOGIN_SUCCESS]: (state, action) => {

    state.isAuthenticated = true
    state.email = action.email

    return state

  },

  [LOGIN_FAILURE]: (state, action) => {
    return setAuthenticated(state)
  },

  [LOGOUT]: (state, action) => {
    return setAuthenticated(state)
  }
})

Then state.isAuthenticated will change to false. But Connector component does not render component "UserFooter" inside.

UserFooter component:

export default (React) => {

  let types = React.PropTypes

  let UserFooter = (props) => {

    console.log('props', props)

    return {

      __proto__: React.Component.prototype,
      props,

      logout (e) {

        e.preventDefault()

        this.props.logout()

      },

      render () {

        let {isAuthenticated, email} = this.props.userFooter

        console.log('render', isAuthenticated)

        return (
          <div>

            {isAuthenticated ? (
              <p>
                Logged as : {email}
                <br/>
                <a onClick={(e) => this.logout(e) }>Sign Out</a>
              </p>

            ) : (
              <Link to='login'>Sign In</Link>
            )}

          </div>
        )
      }
    }
  }

  UserFooter.propTypes = {
    // isAuthenticated: types.bool.isRequired,
    // email: types.string,
    userFooter: types.object.isRequired,
    logout: types.func.isRequired
  }

  return UserFooter

}

Last thing: Amazing work mate!

Hot reload of components failing on some edge cases

I can't pinpoint exactly what is needed for this to happen (I have a pretty complicated setup at the moment), but in some cases I'm getting β€œThis probably means the DOM was unexpectedly mutated” invariant violation on hot reload.

It seems related to hot reloading calling forceUpdate, which in turn causes Provider#componentWillReceiveProps, dispatcher replacing, and setState on the Connector components below in the tree that receive a new select prop.

The simple fix is to put a setTimeout when receiving a new dispatcher in Provider:

        if (redux !== nextRedux) {
          setTimeout(() => {
            const nextDispatcher = nextRedux.getDispatcher();
            redux.replaceDispatcher(nextDispatcher);
          });
        }

I'm not a fan of this solution so I'll just leave this here for posterity and try to figure out the root issue later. Might be a React bug after all.

I want to use "Dumb Components"

But I guess I'm too dumb to understand how. Do the docs actually show anywhere how stores/actions/dispatcher interact when not using all the magicality of smart components and shortcuts like bindActionCreators. If they don't consider this a request for more documentation.

Code splitting in large webapps

I've heard from several people they already want to use this in production, which is a pretty crazy idea, if you ask me πŸ˜‰ . Still, it's better to consider production use cases early. One of such use cases is code splitting.

Large apps don't want to carry all the code in one JS bundle. Webpack and Browserify allow you to split your app into several parts. One core part loads first, the rest is loaded on demand. We want to support this.

Currently Redux forces you to declare all Stores at the root. This is sensible because if you register them lazily as components subscribe to them, some Stores might miss some actions. This is totally not obvious and fragile.

However, defining all Stores at the root robs us of some of the benefits of code splitting, as all Stores will have to be included in the application's entry point bundle.

So here's my proposal. (I haven't thought it through at all; tell me if it's silly.)

I want to support several <Root>s throughout the application. Whenever a new <Root> is mounted under an existing one, instead of initializing a new dispatcher, it adds its stores to the parent dispatcher. They do not receive the actions they missedβ€”fair game IMO.

Open questions:

  • Is the Store state ever destroyed?
  • Should it be destroyed when a child <Root> unmounts?
  • Should it be destroyed when a particular store key is removed from the stores prop?
  • Is it time to rename <Root> to something like <Dispatcher>?

I don't know if it's a good design or not, just something to get the ball rolling.
I want to have some consistent state lifecycle story that works with big apps.

I don't like this idea. If the code loads, it should start handling the actions immediately; not when some view mounts. And what if the new view mounts, and then unmounts? Its <Root> will be gone, poof! But we don't want to erase its state.

Perhaps, a better idea is to rely on React! We got <Root> at the top. (Yo, let's call it <Dispatcher> ;-). Okay, so we got <Dispatcher> at the top. And it has a stores prop. (Not in the decorator version, but we're looking at an advanced use case.) And we got React. And React lets you change props. Get it?

// ------
// Core App
// ------

// StoreRegistry.js

let allStores = {};
let emitChange = null;

export function register(newStores) {
  allStores = { ...allStores, ...newStores }
  emitChange(allStores);
}

export function setChangeListener(listener) {
  if (emitChange) throw new Error('Can set listener once.'); // lol
  emitChange = listener;
  emitChange(allStores);
}

// App.js

import { Dispatcher } from 'redux';
import * as StoreRegistry form './stores/registry';
import * as coreStores from './stores/core';

StoreRegistry.register(coreStores);

class App extends Component {
  constructor(props) {
    super(props);
    StoreRegistry.setChangeListener(() => this.handleStoresChange);
  }

  handleStoresChange(stores) {
    if (this.state) {
      this.setState({ stores });
    } else {
      this.state = { stores };
    }
  }

  render() {
    return (
      <Dispatcher stores={this.state.stores}>
        <SomeRootView />
      </Dispatcher>
    );
  }
}


// ------
// Dynamic module loaded with code splitting
// ------

import * as StoreRegistry form './stores/registry';
import * as extraStores from './stores/extraStores';

// Boom! Will register code-splitted stores.
StoreRegistry.register(extraStores);

// Note that we want to register them when the code loads, not when view mounts.
// The view may never mount, but we want them to start listening to actions ASAP.
// Their state is never destroyed (consistent with the code never being unloaded).

Thoughts?

cc @vslinko

Return value of dispatch

@vslinko

What should redux.dispatch() return? Currently the code is

    function dispatch(action) {
      return typeof action === 'function' ?
        action(dispatch, state) :
        dispatchSync(action);
    }

but I don't remember the circumstances when the return value of the callback form can be useful.

Because the callback form may schedule additional dispatches, it seems that the calling code still doesn't have enough information. Can we just return whatever action was passed?

    function dispatch(action) {
      if (typeof action === 'function') {
        action(dispatch, state);
      } else {
        dispatchSync(action);
      }
      return action;
    }

?

eslint error on return class

Currently showing an eslint error in the decorators, although Babel seems to work it out fine.

If /src/addons/root.js was changed to:

export default function root(stores) {
  return DecoratedComponent => {
    class ReduxRootDecorator {
      static displayName = `ReduxRoot(${getDisplayName(DecoratedComponent)})`;

      render() {
        return (
          <Root stores={stores}>
            {props => <DecoratedComponent {...this.props} {...props} />}
          </Root>
        );
      }
    }
    return ReduxRootDecorator;
  };
}

No eslint errors, babel will assign the IIFE to var, and return it after the class block.

Good idea? Not sure if this is bug with eslint, or an example where you shouldn't return a class due to hoisting.

We need to throw if `select` returns something other than a plain object

<Connector select={fn}> contract is that fn takes the root state and returns a plain object. It is essential for the result to be a plain object because:

  • When using @connect, its result is what you'll get as injected props, and React only supports plain object props at the moment.
  • Redux shallowly compares select return values to determine if an update is needed (a perf optimization).

I think we need to enforce that select returns a plain object. We can use lodash/lang/isPlainObject to perform this check.

PR is welcome (with a test please!).

Support for React Native

I'm currently working on some apps that use React Native. Since this Flux lib really reflects my needs and taste for what I want to do, I would like Redux to be usable in React Native as well. Currently it has a hard dependency on React-web. I believe a similar approach to what's done with Flummox is possible here.

What do you think? I can dive into this in a PR if you decide is good to go but not have enough time to do this.

hot reloading doesn't appear to work with the example

Following the directions:

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

The example app loads/works fine, but changes to the example files don't seem to cause a hot reload.

Can anyone else confirm this?

Thanks!

Use the Airbnb style in the source

Following @DenisIzmaylov's suggestion in #95, I propose we adopt the Airbnb styleguide and change the style if it differs from Airbnb.

If everyone's on board with this, I'd accept a PR that:

  • adds as many Airbnb-enforcing style rules to .eslintrc as possible
  • changes the source and the README to match the styleguide

Thoughts?

Missing usage of 'incrementIfOdd' in example

Hi there,

reading the Readme.md file I see the incrementIfOdd function defined there BUT I cannot make up how to use it and it seems to be not used in the example either - should these actions just be passed as actions to the <Container ...> as well, like this...?

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

Thanks a lot in advance :)

PS: Thanks for starting this new flux implementation - the work you and the awesome contributors have done so far is really inspirational :)

Best async serverside loading technique?

First of all, I love this library and the patterns you guys are using. πŸ‘πŸ‘

I'm trying to use redux to build an isomorphic app. It's working beautifully so far, except that I need to figure out how to wait until my stores have loaded (on the server) before returning the initial page load. Ideally, the loading should take place in the stores themselves, but when I call dispatch(userActions.load()), the store has to return the new state (i.e. return { ...state, loading: true };), so it can't return a promise to wait for. dispatch() returns the action passed to it for some reason. I'd really like something like...

dispatch(someAsyncAction, successAction, failureAction) => Promise

...where the promise doesn't resolve until one of the other two actions is dispatched.

Is that the sort of thing that could be enabled with the middleware pattern?

Am I totally off base and there's a simple way to do this already?

Thanks.

Discussion: handling derived data

@matystl brings up a great point here:

If you have DRY state than this state is usually not best for ui. Derivated data in their essence are pure functions over multiple store data which returns this data combined somehow. So without any further attempt in component you can subscribe to multiple stores and feed their values into this pure function and use result of this in rendering. This is not enough if you want to reuse this in more than one component which are not is child-parent relationship(can be passed as props) or if you don't want to expose this dependency between stores inside rendering and have it outside. With little bit of effort this abstraction can be implemented that derivated data for components will look like store and components can reed them and listen on it. From dispacher point of view after he run action through stores he will recalculate derivated data and only after that will issue change events to component. For performace reason you can use imutable data and caching results of derivated functions.

AFAIK NuclearJS solves this with the concept of Getters.

I don't want to complicate Redux with first-class support for derived data, but it's nice to consider options. Ideally I want something like this as a plugin for Redux. (It could come with a custom Injector that binds to a specific β€œgetter”.)

There's no implementation plans at this point, but I'd love to discuss this and see your ideas!

Explore passing actions in Connector and @connect decorator?

It would be more convenient to not have to call bindActionCreators directly inside the component, so I was thinking that we might able to add something to Connector and the @connect decorator to make this nicer? In addition, that would prevent calling bindActionCreators in render every time (which would provide an extremely slight performance benefit :)).

It seems like the idea of "connecting" could mean connecting to stores AND/OR connecting to actions, if the user wanted. Seems like Connector is perfect to just mean..."hey, connect this component (or child component) to Redux".

Thoughts?

Investigate the library size and dependencies

I haven't given much attention to library size or dependencies yet, as we're still figuring out the right API. However, as people start to depend on it, it's a good idea to check whether we're dragging unnecessary polyfills (babel-runtime?) when it's easily avoidable, and whether there's something we could do to reduce the library size (we can start by adding a UMD build and measuring it! ;-)

I'm currently busy but I'd be happy to see somebody investigate this. If you decide to do it, please let us know in this issue!

How to dispatch many actions in one action creator

I have some example on fluce:

function authFormSubmit(fluce) {
  const {valid, disabled, data: {username, password}} = fluce.stores.authForm

  if (disabled) return

  if (!valid) {
    fluce.dispatch(AUTH_FORM_ERROR, new Error('Form is invalid'))
    return
  }

  fluce.dispatch(AUTH_FORM_DISABLED, true)
  fluce.dispatch(AUTH_FORM_ERROR, null)

  authorize({username, password})
    .then(
      user => fluce.dispatch(CURRENT_USER, user),
      error => fluce.dispatch(AUTH_FORM_ERROR, error)
    )
    .then(
      () => fluce.dispatch(AUTH_FORM_DISABLED, false)
    );
}

How to implement same action creator in redux? Should I create action creator for every action type?

render callbacks + record/replay

I'll get straight to it, will dive into more details if you need.

boring example 1 - local state

class Counter{
  render(){
    return <State initial={0}>{
      (val, set) =>
        <div onClick={() => set(val + 1)}>
          clicked {val} times
        </div>
    }</State>;
  }
}

all state changes with set() will not be reflected while replaying, BUT because it's 'local', you'd still 'start' and 'end' at the correct states.

boring example 2, spring animations

<Spring to={10}>
  {val => <div>{val}</div>}       
</Spring>

this works fine! it might be dicey if you use onSpringUpdate to update something globally AND your timer goes out of sync. Didn't see it happen with simple examples.

boring example 3

<Ajax url='my/ajax/url'>{
   {results => !results ? 'loading...' : `fetched ${results.length} results`}
}</Ajax>

this is tricky. lets say while recording, the request took 2 seconds, and on the 3rd second you did something to change global state based on this. then when replaying, if the request takes 5 seconds, then Putin conquers your apartment with babooshkas. (this is a similar problem to firing ajax requests in stores and trying to do record/replay)

So yeah, it's obvious that issues like this would come up, because 'replay flux' requires state to be managed centrally. That said, this is better than I expected, and I'm happy to recognize the limitations.

cheers!

Shared state

In many cases it is mandatory to share state between components (e.g. sessionStore). How can we achieve that using your library since stores are stateless? Could you add an example or an entry to the README?

remove actions from props

Was wondering how you handle clashing action and store names in containers. But it also strikes me that since actions are just functions without a current state, they don't have to be passed down from the top. Instead, as long as the dispatch function is available as a prop, you'll still be able to do full dispatches. to explain, instead of -

@inject({
  actions: CounterActions,
  stores: { counter: counterStore }
})
export default class Counter {
  render() {
    const { increment, decrement, counter } = this.props;
    return (
      <p>
        <button onClick={increment}>+</button>
      </p>
    );
  }

you could do

@inject({
  stores: { counter: counterStore }
})
export default class Counter {
  static propTypes = {
    dispatch: PropTypes.func.isRequired
  };

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

alternately

        <button onClick={() => dispatch(increment)}>+</button>

but I'm not clear on the benefits (scheduling?)

This would clear up the name clash problem, and make it more 'primitive', if that make sense.

Find better decorator names

I liked previous decorator. While container is acceptable, root is too broad.
Maybe you (@gaearon) could rename root to provides or something similar.
Any thoughts?

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.