Giter Club home page Giter Club logo

prism's Introduction

React / Redux action composition made simple

Given function f and function g we can simply compose them: f ∘ g = f(g()).

Function composition in JavaScript is just a matter of calling function and passing the result as input to other function. This thesis can be applied to React Components and Redux Reducers as well, because functional stateless Component is nothing but function of its props (state of the application) and Reducer is just another plain JavaScript function. Can we compose actions?

Yes we can! Let's just call it action wrapping. Because it's the same principle like you would be wrapping a box inside a box, the last box in the hierarchy contains some object and that's the Action. We can just wrap action type to keep things simple and because action type is just a string, it's as easy as function composition.

const wrapWithFoo = actionType => `Foo.${actionType}`;
const wrapWithBar = actionType => `Bar.${actionType}`;

const composedActionType = wrapWithFoo(wrapWithBar('Baz'));

// Composed action type would be 'Foo.Bar.Baz';

What is it good for?

Imagine you've just implemented your awesome Calendar widget, but then comes a new requirement and you need to have two of those widgets on page so that user can select a date range. With plain old Redux, you need to do two steps:

  1. Isolate state in calendarReducer for particular instance of the Calendar
  2. Tag all the outgoing actions in Calendar component by id of the Calendar

prism ensures that these points are a breeze. Both of the points in particular are nothing but function composition.

prism is useful when you need to instantiate or isolate a UI component, where the UI component is defined as pair of View (React component) and correpsonding State (Redux Reducer).

Installation & Usage

You can install prism via npm.

npm install prism --save

The only peer dependency is React. Install react as well if you haven't done that yet.

Examples

For now, there's just one example showing action composition in action:

  1. Counters Pair

Inspiration

This project was previously called redux-elm and you should still be able to see how those ideas evoled over time in git history. So significiant amount of inspiration has been drawn from The Elm Architecture

prism's People

Contributors

chenxsan avatar dolezel avatar gitter-badger avatar hunterbmt avatar jcheroske avatar jmarceli avatar matthewgertner avatar tomkis 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

prism's Issues

usage with react-router-redux

For anyone looking for react-router integration here is the example:

https://github.com/jmarceli/redux-elm-router

---- ORIGINAL question

Hi, I really like this redux implementation it is really clean and well documented. Anyway I have a problem with routing. How should I refactor the code to get react-router-redux support?

I come up with the following solution:
src/boilerplate.js

import React from 'react';
import { render } from 'react-dom';
import { createStore, compose } from 'redux';
import { Provider, connect } from 'react-redux';
import reduxElm from 'redux-elm';
import { Router, Route, browserHistory } from 'react-router'; // added
import { syncHistoryWithStore } from 'react-router-redux'; // added
import Hello from './hello-world/view'; // added

export default (containerDomId, View, updater) => {
  const storeFactory = compose(
    reduxElm,
    window.devToolsExtension ? window.devToolsExtension() : f => f
  )(createStore);

  const store = storeFactory(updater);

  const history = syncHistoryWithStore(browserHistory, store); // added

  const ConnectedView = connect(appState => ({
    model: appState
  }))(View);

  render((
    <Provider store={store}>
      { /* I wrap ConnectedView inside Router but I'm not sure if it is a good idea */ }
      <Router history={history}>
        <Route path="/" component={ConnectedView}>
          <Route path="/hello" component={Hello} />
        </Route>
      </Router>
    </Provider>
  ), document.getElementById(containerDomId));
}

src/home/updater.js (it is my main application parte executed from main.js)

import { Updater, Matchers } from 'redux-elm';
import gifViewerUpdater, { init as gifViewerInit } from '../gif-viewer/updater';
import { routerReducer } from 'react-router-redux';

export const initialModel = {
  globalCounter: 0,
  routing: routerReducer()
};

export default new Updater(initialModel)
  .case('Increment', model => model + 1)
  .toReducer();

But it doesn't work. Well it works (actions are dispatched), but my app is always on the first page home.
I would appreciate any ideas on how to make routing work.

no HMR

Unfortunately HMR doesn't work, probably because of using stateless functions instead of React components. Are there any known workarounds for this issue?

Here is webpack log info:

[HMR] The following modules couldn't be hot updated: (They would need a full reload!)
log-apply-result.js:13 [HMR]  - 750

Sibling components interaction best practice

Hi guys,

so I was wondering, what is the best practice for making sibling components interact with each other? Imagine I have two components in different branches of a state tree: a form and a button (the button is somewhere else on the page outside the form), and I need, for example, to validate the form on a button click. Like this:

2016-10-17 10 34 34

So far i’ve come up with 2 possible solutions.

The first one is to export an updater-like function from the Form updater.js file along with init and Updater, something like

// Form/updater.js

// export const init...

export const setFormUnderValidation = (state, ...someArgs) => newUnderValidationState

// export default new Updater...

then import it in the parent component updater and use whenever I need to modify the Form validation state. In this case I can store an underValidation flag in the Form model itself and modify it whenever necessary with an exported setFormUnderValidation function. Hovewer, I don't like this approach very much, since if there were to appear more updater-like functions, it would break the whole Updater paradigm (and some practical disadvantages as well, like much less testable and mainteinable code).

The second way would be to store a formUnderValidation flag in the parent component instead and pass it to the Form component in a prop. Like this:

// Parent view
<div>
    <Form underValidation={model.formUnderValidation} />
    <Button onClick={dispatch('Submit')} />
</div>

// Parent updater
    .case('Submit', ...)    /// set formUnderValidation true

// Form view
    // ...
    componentWillReceiveProps(nextProps) {
        if (nextProps.underValidation) { dispatch('Validate') }
        // 'Validate' is a form action to make it validate itself
    }
    // ...

It looks better in a way that it doesn't break a single-Updater paradigm, but is kinda counterintuitive. You give away some control over an object state to its parent, losing some of the component state encapsulation. Moreover, dispatching an action in a lyfecycle method right before render will trigger model update again, which in turn will trigger rerender, breaking a usual rerendering flow.

Could you guys gime me some advice on how to handle this scenario? I think I might be missing some third magical option that would look and feel just right.

Idea behind this repo needs to get more attention

One of the bigest limits I find with classical Redux is it is not fractal (a term that is proposed and nicely explained by @staltz here). I find that to be main reason why, dispite such a strong community, there are no truly reusable and well encapsulated pieces of application (calendars, datagrids, pagers, authenticaiton components, what have you...) that are stateful in a Redux way. Most of community Redux components (middlewares, store enhancers, etc.) are ortogonal to features themselves, but not ready made features or parts of more complex feature, which is imo direct consequence of architecure not being fractal.

This repo proposal eliminates this limit, and makes Redux architecture fractal (which is no suprise since you ported Elm architecture, and Elm is fractal). Key difference, which you nicely bolded in readme, is that beside composing Views, State and Reducers (which are already composed in classical Redux), Actions and Effects should be composed, too. All that leads to composition of application pieces at the higher level. It is really interesting that, usage of organization proposed by this repo, leads to some other (imo high quality) corrections in Redux architecture - elimination of action creators, making middlewares truly orthogonal (unlike those in Redux real world examples), possibility of eliminating action type constants, etc.

Lack of this is my main concern with some new Redux additions, like redux-saga, that is gaining a lot of traction. Despite some very clever ideas and implementation done there, sagas (as is) are not composable in a fractal way with the rest of architecture, and imo are just creating more distraction instead of letting Redux community face this problem.

I wish this repo a best of luck. :)

Peer dependencies?

How come redux-saga and rxjs are peer dependencies of redux-elm?

If the latter imports the former somewhere in the code, shouldn't they just be regular dependencies?

Request to join cdnjs

this is a great lib for redux, and I'm using it with public cdn service, and I can not found it in the cdnjs, would you mind to send redux-elm to cdnjs?

here is the cdnjs/cdnjs

communication between modules seems wrong to me

I like some of the ideas behind (redux-)elm. At the moment I'm using redux with redux saga in a real world application, which is working fine but I'm still looking for a new and better way to do things.

After reading the docs twice I got really scared by the custom matcher chapter. The way this example handles the SUM of the two counters feels absolutely wrong and also complicated to me.

In this example I have to copy the business logic of increment on the parent component. But what happens when the child counter also has some methods like double or reset ? There is no way I can get the SUM of these counters anymore, even if I try to copy all these methods. Also this pattern goes against the trend to normalize things?

The normal Redux way of writing a selector (e.g. SUM = state.a + state.b) feels much safer and easier.

What are your opinions about this? Maybe with the right component structure, we can avoid such use cases... I have to test it for real.

Forms

Like in any other framework there has to be some simple way of handling forms. Until now I was using redux-form but it is not "compatible" with Elm Architecture idea.
What do you think about that?
Do know any alternatives which might be used with redux-elm?

Applying Store Middleware

Hi there,

I'm giving redux-elm a spin and I was wondering if it's possible to add store middleware?

I've tried this to modify the boilerplate.js file with https://github.com/pgte/pouch-redux-middleware:

  const db = new PouchDB('todos');

  const pouchMiddleware = PouchMiddleware({
    path: '/todos',
    db,
    actions: {
      remove: doc => store.dispatch({type: types.DELETE_TODO, id: doc._id}),
      insert: doc => store.dispatch({type: types.INSERT_TODO, todo: doc}),
      update: doc => store.dispatch({type: types.UPDATE_TODO, todo: doc}),
    }
  })

  const storeFactory = compose(
    reduxElm,
    applyMiddleware(pouchMiddleware),
    window.devToolsExtension ? window.devToolsExtension() : f => f
  )(createStore);

  const store = storeFactory(updater);

But it doesn't seem to work.

I'm probably missing something trivial?

Thanks for your time!

Jun

React context access

How can I access React 'context'? https://facebook.github.io/react/docs/context.html
I'm trying to implement form library that is compatible with redux-elm and I would like to pass form name down to fields. I know it's not the best solutions, but at the moment I don't see any other options. Anyways, I realized that I cannot access react context inside components wrapped by view(). I took a look on the source code and it looks that you are not passing second parameter, and it seems to be the origin of my problem.

React-router @@router/LOCATION_CHANGE available in subcomponents Updaters

Use componentWillReceiveProps method

see: example below

--- ORIGINAL QUESTION
One of the issues during react-router integration is detecting route changes which doesn't trigger component unmounting. Example:
I've got routes: orders/1, orders/2, etc. (defined with orders/:id in router)
All of them uses same OrderForm subcomponent so if I switch between them the only action triggered is @@router/LOCATION_CHANGE (triggered by react-router). Unfortunately OrderForm component is nested inside OrderPage component so it doesn't receive that action (because it is not prefixed properly).
My current solution for that is to propagate @@router/LOCATION_CHANGE action through all of the updaters with code like the following:

// this is Updater code
  .case('@@router/LOCATION_CHANGE', (model, action) => (
    {
      ...model,
      login: loginUpdater(model.loginPage, action),
      lostPassword: lostPasswordUpdater(model.lostPasswordPage, action),
      home: homeUpdater(model.homePage, action),
      order: orderUpdater(model.orderPage, action),
    }
  )).toReducer();

Good part is that it works and I can handle @@router/LOCATION_CHANGE in subcomponents but I doesn't look nice and requires such code for every level of nesting.
Does anyone has any better solution?
Setting @@ as global prefix would be nice, so all actions prefixed with @@ will be available for all elm components.
Even better option would be possibility to define (maybe in array) all "global" action prefixes. It will solve future possible problems with other react libraries (e.g. redux-form prefix for redux-form related actions).

Switch to standard boilerplate

Trying to hide boilerplate code behind a custom run function in the examples does more harm than good, in my opinion.

It makes everything less easy to understand when one is used to reading other examples, and disentangling the run pattern in order to incorporate redux-elm into a more customized setup is a bit of a nuisance.

I propose that the examples be changed to standard React + Redux boilerplate.

default export not typechecking

Using flow 0.30.0, redux-elm 3.0.1

src/store/index.js:5
  5: import reduxElm from 'redux-elm';
            ^^^^^^^^ Default import from `redux-elm`. This module has no default export.

Purpose of view wrapper?

Does wrapping my component with view() to create a higher-order component serve any purpose in regard to composition/Elm architecture?

It looks like what it mainly does is dispatching mount/unmount actions and optimizing certain things, which are not essentially related to the fractal pattern.

Apollostack

Hi there,

I was wondering if you had interest in supporting or explaining how redux-elm could be integrated with Apollo? The redux foundation seems like it would allow this in a pretty straightforward way but I'm not clear about how one would go about keeping the elm-ish approach.

Any thoughts?

Cheers!

Jun

Loading initial state and hydrating.

Hi there,

What's the pattern in redux-elm to hydrate/rehydrate which if I understand correctly is the redux terminology for providing initial state from the server or a store (in my case Pouch).

I'm not sure if I can do this in the boilerplate or if it should go in the Updaters and if so how?

Cheers,

Jun

endless loop when TypeError

With React 15 and redux-elm 3, there is a sitation where you get into an endless loop.
If you update gif-viewer-dynamic-list to redux-elm 3 and add an TypeError to the
render function of random-gif-viewer you get an endless loop which gets somehow triggered by the mount dispatch in componentWillMount. This is maybe a react 15 bug, but i could not pinpoint it.

Composition Docs - typos + missing prop

https://github.com/salsita/redux-elm/blob/master/docs/composition/README.md

// invalid code
const ParentView = ({ model }) => ({
  <ChildView model={model.childViewModel} />
});

// correct
const ParentView = ({ model }) => (
  <ChildView model={model.childViewModel} />
);
const ParentView = ({ model }) => ({
  <ChildView model={model.childViewModel} />
});

const ChildView = ({ model, onTextFieldChanged }) => ({
  <input type="text" onChange={onTextFieldChanged} value={model.value} />
});

Then where does the ChildView get its onTextFieldChanged?

Getting component path string

Get it directly from action wrap parameter
example below

-- ORIGINAL QUESTION
Hi,
Is it somehow possible to get component path described by action type inside that component view? It will be very helpful for naming forms in redux-form (ensuring unique name).

Example:
Application state structure: root -> orders -> order
Actions dispatched from order has type of: `Orders.Order.*'

Is it possible to generate Orders.Order string inside Order component view?
I didn't find any way of "extracting" that component path from dispatch method.

Nested routes with react-router

Hi,
During my app development I found that dot restriction in (forwardTo)[https://github.com/salsita/redux-elm/blob/master/src/forwardTo.js#L13-L15] causes some trouble if you want to define nested routes which are also nested in Elm Architecture. Are there any reasons to restrict dot in Action names besides possible naming bugs?

I've made custom deepForwardTo() without that restriction and it seems to be working fine.

Here is sample code from my app:

const deepForwardTo = (dispatch, ...types) => {
  if (types.length === 0) {
    return dispatch;
  } else {
    return action => dispatch(wrapAction(action, ...types));
  }
};

const connectView = (View, modelKey, ...nesting) =>
  // lodash _.get() used for nested model matching
  connect(appState => ({ model: _.get(appState.root, modelKey) }))(
    props => <View {...props} dispatch={deepForwardTo(props.dispatch, ...nesting)} />);

const MainLayout = connectView(Main, 'mainLayout', 'MainLayout');
const OrdersPage = connectView(Orders, 'orders', 'OrdersPage');
const OrderPage = connectView(Order, 'orders.order', 'OrdersPage.Order');

const routerRoutes = [{
  component: MainLayout,
  onEnter: checkLogin,
  childRoutes: [
    {
      path: '/orders'
      indexRoute: { component: OrdersPage },
      childRoutes: [
        { path: '/orders/:id', component: OrderPage },
      ]
    },
  ]
}];

// Exports routes defined in routing file
export default view(({ history }) => (
  <Router history={history} routes={routerRoutes} />
));

It is nice because this way I can define all the routes (even nested ones) in one routing file.

Maybe it won't be bad if we add kind of deepForwardTo to the redux-elm base? This way beginners will get nice error reporting when they use forwardTo and more experienced will have an option to use dots.

[docs] Explain multiple sagas with takeEvery

Hi there,

What's the redux-elm idiomatic way to route events to different sagas?

Right now I do this:

function* saga() {
  yield* takeEvery(['SOMETHING', 'OTHERTHING'], doTHINGS);
}

Where doTHINGS branches to doSOMETHING and doOTHERTHING but I'd like to write something like what case is doing such as:

function* saga() {
  yield* takeEveryCase()
    .case('SOMETHING', doSOMETHING)
    .case('OTHERTHING', doOTHERTHING)
}

Or a variation on this.

Am I missing something obvious?

[feature request] Global actions

As described in #40 it will be nice to have an option of defining global actions. This is handy feature especially for integration with other react/redux libraries as they dispatch non-prefixed actions (e.g. react-router-redux).

I think that there are two options of global actions handling:

  1. Built in redux-elm feature, enabled with some configuration options
  2. Standalone package named e.g. redux-elm-global, which is mounted at the root application updater like any other redux-elm component

I think I would prefer standalone package as it is more modular approach and global actions feature is definitely not required by all redux-elm users.

I've created this issue to gather some opinions about this topic, so please write what do you think about it.

Additional Flow Checking?

What are some other ways to take advantage of static types?

  • Exhaustive pattern match cases on the Updater/Reducer so we make sure to handle each type?
  • Type checking the Views so that only explicitly declared Action types can be dispatched
  • ...

Are any of these possible?

I am trying to evaluate the state of Redux/Flow integration. I've seen this tutorial that provides stronger type checking: http://dchambers.github.io/articles/redux-flow-tutorial/. However, I see the value in the fractal components of redux-elm.

Investigate how to solve LLT

One option would be:

import { saga, mapSaga } from 'redux-elm';

function* onBoardingSaga() {

}

function* updater(appState) {
  yield saga(onBoardingSaga);

  return appState;
}

function* superUpdater(appState) {
  return yield* mapSaga(updater(appState), 'Nested');
}

Does this solve composition issues?

Unit tests examples

I'm 100% sure that unit tests are easy in redux-elm, but it would be nice if docs provide some of examples.

Redux-saga side effects after unmounting component

Conclusion:

Sagas are canceled by redux-elm Updater after unmounting a component

----- ORIGINAL question
I've got a problem which might not be related to redux-elm but I'm not sure so I'll ask.

Code in question looks like that:

function* fetchData() {
  yield put({ type: 'DataLoading' });
  const result = yield call(Effects.getOrders);
  // this line is never executed if component gets unmounted before API returns some results
  yield put({ type: 'DataLoaded', result: result.data });
}

It's responsible for handling API calls and works pretty well as long as I'm waiting for DataLoaded event. If I change active page (with react-router) before data gets loaded then DataLoaded event never fires. It looks like the app doesn't return to the generator function fetchData.
I'm confused and have no idea how to debug this one.
Any hint or tip would be much appreciated.

Can no longer 'put' in certain sagas [3.0.0]

After updating to 3.0.0 (which I've been really looking forward to btw!), some of my sagas are no longer working. It seems to be that the put commands are not sending actions to the redux store. I can replicate this with a trival example. The BLAHBLAH action is visible in devtools in version 2.1.7, but not so in 3.0.0. However, there are some uses of put that still work, and I'm not sure why.

Is this related to this change in redux-saga 0.10.5? https://github.com/yelouafi/redux-saga/releases/tag/v0.10.5

export function* saga() {
  console.log('start Customers saga!!');
  yield put({type: 'BLAHBLAH'});
  yield* takeEvery('Accounts_SubmitQuery', handleQuery);
}

export const update = new Updater(init, saga) {
...
}

[discussion] Saga example contains bad practice?

As i'm looking at section 2.3.1 of the documentation i can't help noticing this line:

const topic = yield select(getTopic);

To my understanding sagas should be state machines and as such they encapsulate their own state. The only data they get (which can contribute to it's state changes) is the one derived from the messages it receives. This is the way in NServiceBus sagas and also the recommended way in the redux-saga documentation. In fact i had to look pretty hard to find select in the redux-saga docs; it gets introduced at some point but never explained and the only place it actually gets explained is the API Reference section. One could say it's kind of hidden in the docs, and the note beneath it is a good explanation why.

Getting back to the redux-elm docs example, it could be easily fixed if the RequestMore action dispatched from the view would carry the topic as payload.

Another option that just crossed my mind is that the example in the docs is intentionally done like that, in which case I'm wondering why.

Publish the pattern matching part stand-alone

I'm really liking what I'm seeing here. I'm usually fairly critical of "Redux alternatives", since their only goal is usually to reduce typing while increasing complexity, but there's a lot of great ideas here, and it's quite elegant, after having played with Elm itself a little.

The thing I'm missing the most in JS is pattern matching, and most of the libraries out there to help with it either have weird APIs or require SweetJS macros.

The idea of a Redux reducer optimized pattern matching lib (as opposed to the silly "use a keyed object instead of a switch options out there) is really appealing to me, and I think with a bit of polish could really stand on its own and make any Redux implementation better.

Just my 2 cents :)

[Performance] Pure view (shallow render)

In gif-viewers-pair example, it should be possible to make random-gif-viewer component pure, so it should rerender only when its model change. For example, when 'Request more' is clicked on 'funny cats' topic and new gif is received, component for 'funny dogs' topic should not be rerendered.

This should be easily achievable with something like recompose pure helper.

But, the problem is that forwardTo calls propagate new instances of dispatch down the component hierarchy.

I made proof of concept implementation of gif-viewers-pair example that tries to solve this problem.
Here is higher order component views should use: higher order component that enables shallow render
Here is example of usage.

Do you think solution to this problem (similar to the one I gave in this quick implementation, or something different) should be also part of redux-elm architecture?

Version 1.x

As many of you have already noticed, there's a next branch which is currently under heavy development.

There're obviously many options we could go (especially for side effects model):

  • side effects using redux-loop
  • side effects using sagas
  • side effects using generators

After many versions we've figured out by trial and error that we want to go the Generators way. We found redux-loop API a way too awkward for use and there's an alternative repo https://github.com/jarvisaoieong/redux-architecture which is using that.

Using Sagas for side effects is nice alternative but composing Sagas is very difficult and there's no silver bullet implementation which would cover all the use cases, therefore Saga composition would have to be done in user land, which is frankly un-acceptable given the fact how difficult it is. Also sagas would ideally be un-aware of app state which discriminates Sagas' usefulness.

Using Generators for side effects definitely has its drawbacks:

  1. All the function that takes callbacks must be generator friendly
  2. You have to explicitly yield* effects when composing them (which some people consider advantage because it's explicit)

We've decided to use Generators because of two reasons:

  1. We are already using that approach in production on pretty complex applications and it simply works
  2. Generators seemed like the least pain from all the options above.

Goals for 1.x

next branch now serves as development branch for 1.x, you can even install it using npm:

npm install [email protected]

Lightweight

We believe that modern application architecture should be based on solid Patterns, not thousand lines of Magic code therefore we'd like to keep redux-elm as lightweight as possible.

Updater Abstraction and Side Effects

The idea is that we will provide Updater abstraction which can be converted into Generator friendly Reducer (we assume that https://github.com/salsita/redux-side-effects is being used).

Pattern matching

Pattern Matching is essential part of this framework and we decided to give User an interface to provide any pattern matching implementation they need. We also provide 3 basic matchers which will serve in 90% of use cases.

Great documentation

Because redux-elm is about patterns, not implementation we are also working on very thorough documentation which should be great starting point for people who are not even experienced with redux or Elm.

Stay tuned!

Using takeEvery executes multiple times for a single action

Trying to pinpoint the issue, but it seems that every time I try to use takeEvery with a simple call, the action is executed multiple times, in my case, it seems that the console.log is run 4 times. My reduced case:

function* rootSaga() {
  yield* takeEvery(SIGN_IN, function*() {
    yield call(() => console.log('do something...'));
  });
}

Running this in a loop instead seems to have the same issue:

function* signIn() {
  while (true) {
    yield take(SIGN_IN);
    yield call(() => console.log('do something'));
  }
}

function* rootSaga() {
  yield fork(signIn);
}

In my view I have a button that is bound to a signIn function via:

const signIn = () => dispatch({ type: 'SIGN_IN' });

Adding a console.log to this method only logs a single time, so the event/dispatch isn't happening multiple times:

const signIn = () => {
  console.log('Signing in...'); // this only logs once
  dispatch({ type: 'SIGN_IN' });
};

Reducer, for clarity:

import { Updater } from 'redux-elm';
import { takeEvery, delay } from 'redux-saga';
import { take, fork, call, put } from 'redux-saga/effects';

const SIGN_IN = 'SIGN_IN';

function* signIn() {
  while (true) {
    yield take(SIGN_IN);
    yield call(() => console.log('take some action...'));
  }
}

function* listen() {
  yield [
    fork(signIn)
  ];
}

export default new Updater({}, listen).toReducer();

I'm using a bit of changes to the boilerplate, so I'll post that as well shortly.

redux-elm-boilerplate

Hi,
Based on https://github.com/mxstbr/react-boilerplate which is (probably) one of the most popular redux boilerplates I manage to create similar repo for redux-elm named redux-elm-boilerplate.

I'm definitely not an expert but I think that my repo might be used as a simple showcase for anyone who is getting started with Elm Architecture in Redux. I've provided all the features available in original react-boilerplate but implemented according to the redux-elm (with some IMO necessary modifications).

Getting any feedback is more than welcome.

Don't Call PropTypes

Hi there,

I'm getting a Don't call PropTypes warning https://facebook.github.io/react/warnings/dont-call-proptypes.html after upgrading to [email protected].

The full warning is:

Warning: You are manually calling a React.PropTypes validation function for the `dispatch` prop on `ReduxElmView`. This is deprecated and will not work in the next major version. You may be seeing this warning due to a third-party PropTypes library. See https://fb.me/react-warning-dont-call-proptypes for details.

Cheers,

Jun

How to pass data to childcomponents or child-redux-elm-modules

Since using redux-elm at work i am still struggling
with some situations concerning data retrieval for childcomponents.

One is how to pass data to deep nested childcomponents.
The concept of containers in redux with which you have access to the
whole store at any nested level does not really work for redux-elm (technically it would work).
So how would i solve a situation where i need to pass props down the hierachy, without
passing it down manualy. Using React Context would be a solution.

Another problem is with related/required data. tomkis1 metioned a
entityrepository which is included in every model of redux-elm
moduls which subscribe to it. So every
redux-elm module can access all entities and retrieve needed entities.
But this would not solve all use case, because if required data is not an entity (like selected date)
i would still have to pass in down as props.

appreciate any thoughts.

Name of the library

When I was presenting redux-elm @ reactive vienna I guess that @ryyppy had a good point about inconsistency between redux-elm and elm 17+ which has removed some concepts (FRP) and did slight changes in component composition. Function forwardTo has been removed and replaced by App.map which goal is technically still the same.

Anyway, after re-thinking everything we can now declare that redux-elm and elm shares just idea of fractability by utilizing component composition. This is hardly the most important part of Elm.

The long term goal has always been renaming the library and distancing from original Elm architecture since we don't want to blindly copy what already exists.

I filed this issue and would appreciate any feedback on this + possible name suggestions.

Cancel saga after component is unmounted

I see sagas are only started when mounting components but not stopped when they are unmounted. This can introduce data corruption when navigating from component C1 to component C2 of the same type.

Issue with a specific unmounting situation

I came across a situation where a component
is not getting unmounted with the same actionPredix as it was mounted before.
I build an example 'dynamic-tab-of-lists' where each list is an instance of 'dynamic-list-of-counters'
The idea is to switch between them in a tabmenu.
Now it works when you provide the key property with the index of the list, but this destroys and rebuilds the whole tree on each switch.
Without the key the composition of dispatch in the forwardTo function changes (on tab-switch) and the components that gets unmounted wont have the right actionPrefix in their unmount action.
It does not look like this can be solved with the current saga initialization process (mount/unmount) at least as far I could see.

https://github.com/namjul/redux-elm/tree/tabs-example/examples/dynamic-tab-of-lists/src/dynamic-tab-of-lists/view.js#L48

In verion 1.x.x this worked.

Preceding discussion on Gitter: https://gitter.im/salsita/redux-elm?at=574d97c180352f204df3c28e

Dynamic component composition

Hi, there!

Maybe I have a very stupid question but I can't understand how to make multiple components composition when the list of components is dynamically changes.
For example I have two components:

Component
it's view:

export default view(({ model, dispatch }) => (
    <div onClick={() => dispatch({type: 'CLICK', payload: model.content}>
        {model.content}
    </div>
))

ComponentList
is's view:

export default view(({ model, dispatch }) => (
    <ul>
        <li>
            <Component model={{content: model.a}} dispatch={forwardTo(dispatch, 'A')} />
        </li>
        <li>
            <Component model={{content: model.b}} dispatch={forwardTo(dispatch, 'B')} />
        </li>
    </ul>
))

it's updater:

export default new Updater({a: null, b: null})
    .case('A', (model, action) => {...model, a: ComponentUpdater(model.a, action)}
    .case('B', (model, action) => {...model, b: ComponentUpdater(model.b, action)}
    .toReducer();

It is totally clear for me. But what should I do, if I have dynamically changed component list. Eg:

export default view(({ model, dispatch }) => (
    <ul>
        {model.list.map((item, index) => 
            <li>
                <Component model={{content: item}} dispatch={forwardTo(dispatch, index)} />
            </li>
        )}       
    </ul>
))

How should my ComponentList updater look like?

flowtype checking with v0.28.0

Flow broke for me when I updated. Here's what I'm getting when I run:

node_modules/redux-elm/lib/index.js.flow:5
  5: export type StatelessReactComponent = (props : Object) => ReactElement;
                                                               ^^^^^^^^^^^^ identifier `ReactElement`. Could not resolve name

node_modules/redux-elm/lib/index.js.flow:6
  6: export type Component = ReactComponent | StatelessReactComponent;
                             ^^^^^^^^^^^^^^ ReactComponent. Application of polymorphic type needs <list of 3 arguments>. (Can use `*` for inferrable ones)

node_modules/redux-elm/lib/index.js.flow:24
 24:   constructor(initialModel : M, saga? : () => Generator, matcher? : Matcher) : Updater<M>;
                                                   ^^^^^^^^^ Generator. Application of polymorphic type needs <list of 3 arguments>. (Can use `*` for inferrable ones)

node_modules/redux-elm/src/index.js.flow:5
  5: export type StatelessReactComponent = (props : Object) => ReactElement;
                                                               ^^^^^^^^^^^^ identifier `ReactElement`. Could not resolve name

node_modules/redux-elm/src/index.js.flow:6
  6: export type Component = ReactComponent | StatelessReactComponent;
                             ^^^^^^^^^^^^^^ ReactComponent. Application of polymorphic type needs <list of 3 arguments>. (Can use `*` for inferrable ones)

node_modules/redux-elm/src/index.js.flow:24
 24:   constructor(initialModel : M, saga? : () => Generator, matcher? : Matcher) : Updater<M>;
                                                   ^^^^^^^^^ Generator. Application of polymorphic type needs <list of 3 arguments>. (Can use `*` for inferrable ones)

saga not canceled when removing item

I tested it with the dynamic-list-of-counters example. Updating it to redux-elm 3 and adding a saga to the counter i get the following situation.
Adding a counter after i already added and removed one, it throws the following error:

'The Saga instance has already been mounted, this basically mean that your Updaters do not form a tree, please be sure that every Updater is wrapped by another Updater (except root updater). It does not make sense to use combineReducers for Updaters.'

During the removal it does not seam to cancel the saga, so that it is still in the SagaRepository.

Custom action matchers in Saga

Custom action matchers are available in the Updater but not in Saga. As I understand it, at the moment the saga can only take() an action that is local to the component (e.g. wrapped).

One use case would be a global 'REFRESH' action fired in the app that would need each component saga to fetch their data again.

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.