Giter Site home page Giter Site logo

makeomatic / redux-connect Goto Github PK

View Code? Open in Web Editor NEW
550.0 22.0 69.0 1.91 MB

Provides decorator for resolving async props in react-router, extremely useful for handling server-side rendering in React

License: MIT License

JavaScript 100.00%
react redux-connect server-side-rendering immutablejs react-redux isomorphic universal-react node nodejs

redux-connect's Introduction

ReduxConnect for React Router

npm version Build Status

How do you usually request data and store it to redux state? You create actions that do async jobs to load data, create reducer to save this data to redux state, then connect data to your component or container.

Usually it's very similar routine tasks.

Also, usually we want data to be preloaded. Especially if you're building universal app, or you just want pages to be solid, don't jump when data was loaded.

This package consist of 2 parts: one part allows you to delay containers rendering until some async actions are happening. Another stores your data to redux state and connect your loaded data to your container.

Notice

This is a fork and refactor of redux-async-connect

Installation & Usage

Using npm:

$ npm install redux-connect -S

import { BrowserRouter } from "react-router-dom";
import { renderRoutes } from "react-router-config";
import {
  ReduxAsyncConnect,
  asyncConnect,
  reducer as reduxAsyncConnect
} from "redux-connect";
import React from "react";
import { hydrate } from "react-dom";
import { createStore, combineReducers } from "redux";
import { Provider } from "react-redux";

const App = props => {
  const { route, asyncConnectKeyExample } = props; // access data from asyncConnect as props
  return (
    <div>
      {asyncConnectKeyExample && asyncConnectKeyExample.name}
      {renderRoutes(route.routes)}
    </div>
  );
};

// Conenect App with asyncConnect
const ConnectedApp = asyncConnect([
  // API for ayncConnect decorator: https://github.com/makeomatic/redux-connect/blob/master/docs/API.MD#asyncconnect-decorator
  {
    key: "asyncConnectKeyExample",
    promise: ({ match: { params }, helpers }) =>
      Promise.resolve({
        id: 1,
        name: "value returned from promise for the key asyncConnectKeyExample"
      })
  }
])(App);

const ChildRoute = () => <div>{"child component"}</div>;

// config route
const routes = [
  {
    path: "/",
    component: ConnectedApp,
    routes: [
      {
        path: "/child",
        exact: true,
        component: ChildRoute
      }
    ]
  }
];

// Config store
const store = createStore(
  combineReducers({ reduxAsyncConnect }), // Connect redux async reducer
  window.__data
);
window.store = store;

// App Mount point
hydrate(
  <Provider store={store} key="provider">
    <BrowserRouter>
      {/** Render `Router` with ReduxAsyncConnect middleware */}
      <ReduxAsyncConnect routes={routes} />
    </BrowserRouter>
  </Provider>,
  document.getElementById("root")
);

Server

import { renderToString } from 'react-dom/server'
import StaticRouter from 'react-router/StaticRouter'
import { ReduxAsyncConnect, loadOnServer, reducer as reduxAsyncConnect } from 'redux-connect'
import { parse as parseUrl } from 'url'
import { Provider } from 'react-redux'
import { createStore, combineReducers } from 'redux'
import serialize from 'serialize-javascript'

app.get('*', (req, res) => {
  const store = createStore(combineReducers({ reduxAsyncConnect }))
  const url = req.originalUrl || req.url
  const location = parseUrl(url)

  // 1. load data
  loadOnServer({ store, location, routes, helpers })
    .then(() => {
      const context = {}

      // 2. use `ReduxAsyncConnect` to render component tree
      const appHTML = renderToString(
        <Provider store={store} key="provider">
          <StaticRouter location={location} context={context}>
            <ReduxAsyncConnect routes={routes} helpers={helpers} />
          </StaticRouter>
        </Provider>
      )

      // handle redirects
      if (context.url) {
        req.header('Location', context.url)
        return res.send(302)
      }

      // 3. render the Redux initial data into the server markup
      const html = createPage(appHTML, store)
      res.send(html)
    })
})

function createPage(html, store) {
  return `
    <!doctype html>
    <html>
      <body>
        <div id="app">${html}</div>

        <!-- its a Redux initial data -->
        <script type="text/javascript">
          window.__data=${serialize(store.getState())};
        </script>
      </body>
    </html>
  `
}

Usage with ImmutableJS

This lib can be used with ImmutableJS or any other immutability lib by providing methods that convert the state between mutable and immutable data. Along with those methods, there is also a special immutable reducer that needs to be used instead of the normal reducer.

import { setToImmutableStateFunc, setToMutableStateFunc, immutableReducer as reduxAsyncConnect } from 'redux-connect';

// Set the mutability/immutability functions
setToImmutableStateFunc((mutableState) => Immutable.fromJS(mutableState));
setToMutableStateFunc((immutableState) => immutableState.toJS());

// Thats all, now just use redux-connect as normal
export const rootReducer = combineReducers({
  reduxAsyncConnect,
  ...
})

Comparing with other libraries

There are some solutions of problem described above:

  • AsyncProps It solves the same problem, but it doesn't work with redux state. Also it's significantly more complex inside, because it contains lots of logic to connect data to props. It uses callbacks against promises...
  • react-fetcher It's very simple library too. But it provides you only interface for decorating your components and methods to fetch data for them. It doesn't integrated with React Router or Redux. So, you need to write you custom logic to delay routing transition for example.
  • react-resolver Works similar, but isn't integrated with redux.

Redux Connect uses awesome Redux to keep all fetched data in state. This integration gives you agility:

  • you can react on fetching actions like data loading or load success in your own reducers
  • you can create own middleware to handle Redux Async Connect actions
  • you can connect to loaded data anywhere else, just using simple redux @connect
  • finally, you can debug and see your data using Redux Dev Tools

Also it's integrated with React Router to prevent routing transition until data is loaded.

Contributors

Collaboration

You're welcome to PR, and we appreciate any questions or issues, please open an issue!

redux-connect's People

Contributors

albertwhite avatar aleksxor avatar andrewmclagan avatar arkist avatar avvs avatar bannier avatar bkdev98 avatar eliseumds avatar horizonxp avatar ioanlucut avatar jorrit avatar joshka avatar narigo avatar nfriedly avatar ruiaraujo avatar thomasdrakebjss avatar toxahak 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

redux-connect's Issues

Using with a wrapper

I would like to know if it is possible to use redux-connect with a wrapper.

To only render a component for logged in users, I created a higher order component:

import React from 'react';
import { connect } from 'react-redux';

export default function authWrapper(options = {}) {

  return (WrappedComponent) => {
    const AuthComponent = ({ user }) => {
      if (options.guest) {
        return (
          user ? <div>You are already logged in!</div> : <WrappedComponent />
        );
      } else {
        return (
          user ? <WrappedComponent /> : <div>Please sign in</div>
        );
      }
    };

    return connect(
      (state) => ({
        user: state.auth.user
      })
    )(AuthComponent);
  };
}

I am using it like the following:

const ensureLoggedIn = authWrapper();

export default () => (
  <Route path="/" component={App}>
    <Route path="/cakes" component={ensureLoggedIn(Cakes)} />
  </Route>
);

I decorated Cakes component with @asyncConnect using redux-connect to fetch some data. Now that Cakes is wrapped by another component, redux-connect ignores the promises bound to Cakes.

What can I do do keep using my higher order component and redux-connect together?

Getting empty result when rendered on server

I am trying to use redux-connect for server side rendering. I have the following code for it (similar to this example):

...
import { ReduxAsyncConnect, loadOnServer } from 'redux-connect';
...
      loadOnServer({ ...renderProps, store }).then(() => {
        const component = (
          <Provider store={store} key="provider" >
            <ReduxAsyncConnect {...renderProps} />
          </Provider>
        );

        res.status(200);

        global.navigator = {userAgent: req.headers['user-agent']};

        res.send('<!doctype html>\n' +
          renderToString(<Html assets={webpackIsomorphicTools.assets()} component={component} store={store} />
        ));
      });
...

But component is empty when I call renderToString with it.

The same code works when I use redux-async-connect v1.0.0-rc2. But in the latest 1.0.0-rc4, the same issue happens.

What am I missing?

@asyncConnect - does not work when changing the route

Hi guys!

During startup of my application, "@asyncConnect" works and receives data from the api server, but when i change current route(changing to another page), there "@asyncConnect" not working, why?

update:

After server side rendering is happening with correct redux state tree(when switching to a different page), client side is again making the API call and re rendering of react component is happening. Any idea how to avoid this?

usage as a HOC

From what I can tell it is possible to use asyncConnect as a higher order component, instead of as a decorator, but this is not documented anywhere AFAICT.

The reason why you might prefer a HOC to a decorator is that decorators have been a bit unstable. For example, they just reached stage-2 in es7 (with breaking changes) and still does not have an official babel plugin.

An example of usage as a HOC would be:

import {connect} from 'react-redux'
import {asyncConnect} from 'redux-connect'

const Comp = ({info}) => (
    <h1>{info.id} {info.name}</h1>
)

const connectedComp = connect(
    mapStateToProps,
    mapDispatchToProps
)(Comp)

const connectedAsyncComp = asyncConnect([{
    key: 'info',
    promise: ({ params, helpers }) => Promise.resolve({ id: 1, name: 'Borsch' })
}])(connectedComp)

AFAICT, it works just fine using this as an HOC, and in this case I think it should be documented. If this usecase is not supported, please let me know.

Higher order components?

I have a higher order component which handles protecting authenticated pages. It looks something like this:

export default function requireAuthentication(Component) {
  componentWillMount() {
    // check if user is logged in. redirect to login page if not
  }

 render () {
   const userIsLoggedIn = // something
   return (
     <div>
      {userIsLoggedIn ? <Component {...this.props} /> : null}
    </div>
   )
 }
}

Then, in routes.js I have:

return (
   ...
  <Route component={requireAuthentication(MyComponent)} path="/foo" />
  ...
)

When I use this requireAuthentication function, my asyncConnect promise is never called. When I remove the requireAuthentication, it is called. Is there a way I can get this to work?

END_GLOBAL_LOAD never fires

I would like to know why END_GLOBAL_LOAD never fires while rendering my component.

@asyncConnect([{
  promise: ({ store: { dispatch, getState }, helpers, params }) => {
    const promises = [];
    // there are other promises but I commented them out for debugging
    promises.push(dispatch(fetchCompanies(helpers.client)));
    return Promise.all(promises);
  }
}]
class Home extends React.Component {
...

Here is the action dispatched:

export function fetchCompanies(client) {
  return (dispatch, getState) => {
    dispatch(requestCompanies()); // `requestCompanies` just returns an action object

    return client.get(`/companies/) // calls api and returns a promise
      .then(json => {
        dispatch(receiveCompanies(json.companies, json.numResults));
      }).catch(e => console.log('network error', e));
  };
}

Whenever the component is re-rendered, I see that "@reduxAsyncConnect/BEGIN_GLOBAL_LOAD" is fired, but I don't see END_GLOBAL_LOAD. What am I missing?

Redux connect install

I try to install redux-connect via npm on my boilerplate and I've get errors:

npm install redux-connect -S
npm ERR! Darwin 14.3.0
npm ERR! argv "/usr/local/bin/node" "/usr/local/bin/npm" "install" "redux-connect" "-S"
npm ERR! node v4.4.7
npm ERR! npm  v2.15.8
npm ERR! code EPEERINVALID

npm ERR! peerinvalid The package [email protected] does not satisfy its siblings' peerDependencies requirements!
npm ERR! peerinvalid Peer [email protected] wants react@^15.3.0
npm ERR! peerinvalid Peer [email protected] wants react@^0.14.0 || ^15.0.0-0
npm ERR! peerinvalid Peer [email protected] wants react@^0.14.0 || ^15.0.0
npm ERR! peerinvalid Peer [email protected] wants react@^15.0.0-0
npm ERR! peerinvalid Peer [email protected] wants react@>= 0.9.0
npm ERR! peerinvalid Peer [email protected] wants react@^15.3.0
npm ERR! peerinvalid Peer [email protected] wants [email protected]
npm ERR! peerinvalid Peer [email protected] wants [email protected] || 15.x.x
npm ERR! peerinvalid Peer [email protected] wants [email protected] || 0.14.x || ^15.0.0-0

I have the lastest versions of react-* packages

using @asyncConnect @and connect

Hi,

I see now the @asyncConnect supports non-promises on the promise key of the 'asyncItems' objects.

When/why would one want to do this?

Only reason I can think is to reduce code by having only one decorator (@asyncConnect) rather than two (@connect and @asyncConnect).

thanks

can't read the property "loaded" of underfined

I'm using redux with immutable js.

TypeError: Cannot read property 'loaded' of undefined
[1]     at AsyncConnect.isLoaded (/node_modules/redux-connect/lib/components/AsyncConnect.js:67:59)

Awesome ...

I was about to fork the original repo. Saw this in the fork network graph... thanks so well done!

Accessing query params from React-router

Hi,

I've been facing problems accessing the query params from the router. The reduxAsyncConnect method exposes the params argument which has only the declared arguments but not the query values. I need them in the static method for server access. How do I do this?

Future plans question

Hello,

Will this project from now on be maintained independently from redux-async-connect or will it be eventually merged back? Will any future redux-async-connect changes be merged or otherwise integrated here?

Thanks

Exceptions are being absorbed

Just a heads up, exceptions seem to be silent for anything inside the loadOnServer callback.

Example

image

view here is suppose to be a component, but I rendered it as a string beforehand

I put a try/catch inside the callback, so I could find out this was happening:

{ [Invariant Violation: renderToString(): You must pass a valid ReactElement.] name: 'Invariant Violation', framesToPop: 1 }

IndexRedirect bug?

I seem to have some trouble with this setup:

<Route name="Home" path="/" component={App}>
    <IndexRedirect to="articles" />
    <Route name="Articles" path="articles">
        <IndexRoute component={Articles} />
        <Route path=":article" components={ArticleItem} />
    </Route>
</Route>

With a straight forward AsyncConnect on Articles

function mapStateToProps(state) {
  return {
    articles: state.articles
  }
}

function mapStateToPropsAsync({store: {dispatch}}) {
  return dispatch(fetchArticles())
}

const asyncItems = [{
  promise: mapStateToPropsAsync
}]

export default asyncConnect(asyncItems)(connect(mapStateToProps)(Articles))

Everything runs smooth when I go to /articles
But when I go to root, I get this error (but everything still renders fine):

TypeError: Cannot read property 'length' of undefined

citing at eachComponents (node_modules/redux-connect/lib/helpers/utils.js:72:21)
at filterAndFlattenComponents (node_modules/redux-connect/lib/helpers/utils.js:98:3)
at loadAsyncConnect (node_modules/redux-connect/lib/helpers/utils.js:121:19)
at loadOnServer (node_modules/redux-connect/lib/helpers/utils.js:169:10)
at reactMiddleware.js:20:5
at node_modules/react-router/lib/match.js:65:5
at handleErrorOrRedirect (node_modules/react-router/lib/createTransitionManager.js:124:39)
at finishEnterHooks (node_modules/react-router/lib/createTransitionManager.js:109:41)
at next (node_modules/react-router/lib/AsyncUtils.js:46:16)
at loopAsync node_modules/react-router/lib/AsyncUtils.js:56:3)

Is this my wrong-doing or is there a bug with IndexRedirect?

ReduxConnect breaks hot reloading with server side rendering

Hello
Yesterday I changed redux-async-connect to redux-connect in my project and I noticed that it broke hot reloading. Part of code that was calculated on server side never changes, in opposite to client side.
I'm using this project: https://github.com/erikras/react-redux-universal-hot-example as my boilerplate and on clean version of this project it works fine (I added console.log to code and after hot reload it showed my message), when I changed there redux-async-connect on redux-connect I did the same thing - and in order to display my console.log in terminal I had to quit process and start it again, just refreshing page wasn't enough.

Doesn't load data anymore after replacing redux-async-connect with redux-connect

I'm trying to switch from redux-async-connect to redux-connect.
I migrated as done in this PR erikras/react-redux-universal-hot-example#1140

The problem is that once I replace all from 'redux-async-connect in import statements with redux-connect it doesn't fetch any data anymore.

This is part of an example page where I want to use redux-connect:

import React, {Component, PropTypes} from 'react';
import Helmet from 'react-helmet';
import { asyncConnect } from 'redux-async-connect';
import {connect} from 'react-redux';

@asyncConnect([{
  deferred: true,
  promise: ({
    store: {
      dispatch,
      getState,
    }
  }) => {
    if (!isLoaded(getState())) {
      return dispatch(load());
    }
  }
}])
@connect(
  state => ({
    items: state.invoice.data,
    error: state.invoice.error,
    loading: state.invoice.loading,
    loaded: state.invoice.loaded,
    messages: state.intl.messages,
  }),
  dispatch => ({dispatch})
)

Clarify filter (item.deferred) logic

Hi @AVVS! I understand how to use redux-connect to block rendering of the component until data is loaded and if I don't care about blocking, I end up going with componentDidMount to load data.

What I am still unclear about, and this maybe a noob question - is how to defer some data and not others? In various other threads, I see you mention the filter property but I wasn't sure how that exactly worked. Like on the main page, I do have something like this on the client.js code

filter={item => !item.deferred}

Maybe this is just a documentation issue, but I'd love to understand how the filter code works and how can I get the some data to block and others to defer. Here is what my code looks like today:

@asyncConnect(
  [{
    promise: ({ location, params, store: { dispatch, getState } }) => {
      if (!isPostLoaded(getState(), params.postSlug)) {
        return dispatch(loadContent(params.postSlug));
      }
    }
  }],
  selectorList,
  mapDispatchToProps
)

Thank you!

Decorator is not being called

I am wondering why my decorators are not being called. From the code, it looks like the decorator should add reduxAsyncConnect static property to my component but it is not.

import React from 'react';
import { asyncConnect } from 'redux-connect';
...
@asyncConnect([{
  key: 'lunch',
  promise: ({ params, helpers }) => Promise.resolve('Borsch')
}])
class App extends React.Component {
...

On server, I am doing the same thing as specified in the guide. The server renders the component but components does not have reduxAsyncConnect property, and hence redux-connect doesn't work for me. I examined this part and the flattened array is empty, and components is an array of my components to be rendered.

loadOnServer({ ...renderProps, store }).then(() => {
...
});

What am I missing?

asyncConnect fires everytime I navigate to anther page.

I am using server side rendering.

I have my main App

<Route path="/" component={App}>
            <IndexRoute component={Index} />
            <Route path="/gifs" component={Gifs} />

            { /* Catch all route */ }
            <Route path="*" component={NotFound} status={404} />
</Route>

in this app I have

@asyncConnect([{
    promise: ({store: {dispatch, getState}}) =>  dispatch(actionGetAppData())
}])
@connect(state => ({app: state.app}))

I am using a thunk to perform the call and return a promise

async function getAppData() {
    try{
        const data = await fetch(APP_DATA_LOCATION).then(result => result.json());
        return data;
    }catch(error) {
        console.error('[ERROR] - getAppData:', error);
        return error;
    }
}

export function actionGetAppData() {
    return (dispatch) => {
        dispatch({type: APP_DATA_GET});
        return getAppData().then(
            data => dispatch({type: APP_DATA_SUCCESS, data}),
            error => dispatch({type: APP_DATA_FAIL, error})
        );
    };
}

everytime I navigate / -> /gif and /gif -> / it calls this action.
Is this what is supposed to happen? Am I in charge of the conditional logic whether or not to load this call after the first time?

Thanks

How can I avoid making multiple API calls?

I am calling an API whose payload includes multiple key value pairs. I want to connect multiple properties of the payload to a component.

But I have to make multiple API calls to do so. Suppose /companies API endpoint returns

{companies: [/*array of companies max length 30*/], numResults: /*total number of results*/}

I find myself doing the following:

@asyncConnect([
  {
    key: 'companies',
    promise: ({ params, helpers }) => {
      return helpers.client.get('/companies').then(res => res.companies)
    }
  },
  {
    key: 'numResults',
    promise: ({ params, helpers }) => {
      return helpers.client.get('/companies').then(res => res.numResults)
    }
  }
]);
class Home extends React.Component {
...

Any ways to avoid calling the same API multiple times to use different results from the payload?

Reloading data

Firstly, thanks for creating this fork.

How would one go about refetching data? E.g. after posting some update, invalidate the current response for some key and rerun the promise. Currently I am just using react-router-redux -> replace (currentLocation) but this seems particularly hacky.

Thoughts?

Some confusion about store state.

Hello, thanks for addressing this problem with routing and data fetching!

However, I am confused about managing state after it is loaded. Do I understand correctly that if I want to manage/update state in my store of a userProfile I need to capture it from a LOAD_SUCCESS action? I ask because the loading actions don't seem to be public exports.

const userProfile = (state = {}, { type, payload }) => {

  if (type !== LOAD_SUCCESS && type !== PROFILE_UPDATE) return state

  if (type === LOAD_SUCCESS && payload.key === 'userProfile') return payload.data

  return { state, ...payload } 
}

Does that look right?

new react-router applyRouterMiddleware method

Hi,

Just wondering, how the code would be changed with the new applyRouterMiddleware method from react-router.

Currently in the example we have

<Router render={(props) => <ReduxAsyncConnect {...props}/>} history={browserHistory}>

Thinking we should have something like

function useReduxConnect() { return { renderRouterContext: (child, props) => ( <ReduxAsyncConnect {...props} /> ) }; }

and then

<Router render={applyRouterMiddleware(useReduxConnect()} />} history={browserHistory}>

But not really sure.

Support server error

currently when server render happens whether if it is was successful or not it will render then page with response status 200, but what if one of the promises failed? we should have the ability to catch it and return server error.

loadOnServer({ ...renderProps, store }).then(() => { SUCCESS }).catch(() => { FAILURE })

Getting a warning on 2.4.0

After migrating to this fork from the original redux-async-connect package, I have encountered a problem with getting a warning with the text of "warning.js:44Warning: setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op. Please check the code for the undefined component."
Additional screenshot is provided.
screenshot 2016-05-31 05 03 50

Downgrading to 2.3.0 removes the problem.
Hope it helps!

Chain promises of ajax calls

Is it possible to chain promises with a depency between them?
For example, I need to fetch a user by username to get his id to fetch his chats

Like:

export default asyncConnect([{
  key: 'user',
  promise: ({ store: { dispatch, getState }, params: { username } }) => {
    return dispatch(getUserByUsername(username))
  }
}, {
  key: 'chat',
  promise: ({ store: { dispatch, getState },  user: { _id }}) => {
    return dispatch(getChat(_id))
  }
}]

when navigating away from asyncConnect container, that container's asyncConnect and render is called

I have a page that uses (see screen grab below for chrome's console output)

....
  render() {
    const { table, tableName, regions, sizes, types } = this.props;
    console.log('TablePage tableName', tableName);
....
export default asyncConnect([
    ...some promises
  ],
  state => {
    const tableName = getTableName(state);
    console.log('TablePage asyncConnect tableName', tableName);
    return {
      table: state[tableName],
      tableName: tableName,
    };
  },
)(TablePage);

(I get the tableName from the page URL path.) When I navigate away from this pages (there is no 'tableName' in the path and therefore the table cannot be found in the state), I get errors that complain about failed PropTypes for that page, e.g. table - Warning: Failed propType: Required prop tablewas not specified inTablePage. Check the render method of Connect(TablePage).

Why would the new/next page be triggering the 'navigated from' page's asyncConnect? Is this a bug or fact of life? Any way to fix this currently?

Or, why would redux-connect's @reduxAsyncConnect/BEGIN_GLOBAL_LOAD be triggerred on a page that is not using asyncConnect -- is there someway to not have the global load trigger on all page transistions?

The AdminPage and TablePage routes look like the following, although the error occurs when navigating away from a TablePage to any other page.

      <Route component={AdminPage} path="admin">
        <Route component={AdminTablePage} path="tables/:table/:activePage" />
      </Route>

screen shot 2016-06-17 at 10 17 36 am

why add the data to the redux-connect store?

In https://github.com/makeomatic/redux-connect/blob/master/modules/store.js

  [LOAD_SUCCESS]: (state, { payload: { key, data } }) => ({
    ...state,
    loadState: {
      ...state.loadState,
      [key]: {
        loading: false,
        loaded: true,
        error: null,
      },
    },
    [key]: data,
  }),

Why add the requested data ([key]: data,) to the reduxAsyncConnect store state? Shouldn't this go to an action's targeted reducer? Just trying this out and I am using one of my own actions->reducers to set my store's state, but this data is also duplicated in the reduxAsyncConnect store's state. Is the intention to just keep everything in reduxAsyncConnect store's state and we should not be using our reducers for redux-connect 'stuff'?

support for defer: true

In redux-async-connect I was able to utilize a defer: true option in order to defer the fetching of the data until after the view has transitioned. After switching to redux-connect it seems like that use case is no longer supported.

Are there plans to add this functionality back in? My specific use case is that when I fetch an external entity I keep its data in my store along with a loaded flag and only fetch the data if loaded is false . In some cases when I transition away from this entity's view I will set its loaded flag back to false, but not clear out the data. The result is that if I navigate back to that view I'm able to immediately display previously loaded data while the fresh data is fetched from the server, resulting in a much faster perceived page speed.

Admittedly, I'm not sure how much of that logic was built into redux-async-connect and how much of it was manually implemented via the react-redux-universal-hot-example that my app is based off of. All I can say for sure is that when switching to redux-connect if you navigate to a page with deferred: true then the data loads fine if its coming from the server, but if its a page transition in the client the data never loads.

filter-ed promises not being executed in componentDidMount

BEGIN_GLOBAL_LOAD/END_GLOBAL_LOAD are dispatched on the client side even when filter stops the asyncConnect from blocking the render function. However, once the component is mounted, since the redux has saved the state loaded as true, it does not fetch the data. Due to this you have to manually redo the logic for re-fetching data in these instances. It would be nice to have some logic to ascertain that the promise is executed in componentDidMount.

Not firing on my component

I don't know if this is a bug or not. The asyncConnect decorator works on some components but it doesn't on other.

I have this component that is supposed to fetch some remote categories and construct a dropdown out of them. The component is placed inside a form which form is placed inside a container so this makes me think that maybe asyncConnect works only on components directly related to the routes maybe and not to nested components.

@asyncConnect([{
    promise: ({store: {dispatch, getState}}) => {
        const promises = [];
        console.log('asdasdasd');
        return Promise.all(promises);
    }
}])
export default class CategoriesDropdown extends Component {
        render() {
        const _this = this;

        return (
            <div>test</div>
        );
    }
}

I have placed a console.log inside there to see if the decorator is called whatsoever without any results. Any ideas why isn't the decorator firing?

Add example without decorators

Not everyone uses them

As an example here's how we use redux-async-connect (0.1) in our app:

class DashboardRoute extends React.Component<IDashboardRouteProps, {}> {
  static reduxAsyncConnect(params, store) {
    return Promise.all([store.dispatch(getDashboardData())]);
  }
}

What would be the equivalent in redux-connect? getDashboardData is a redux action here so we need the dispatch as well

Handle "asyncConnect" decorated containers that are hidden in the children components

I have a nested structure of components (containers). Each has it's own asyncConnect. The top one can hold up to N number of children. These children can come from a CMS. The schema is well defined and each maps to one of these children.

Let's say the top level one would load available categories in an API call. Then the children ( sub-categories ) can be included dynamically, and each would worry about it's own content, separately, independent of the top level container ( other than a route parameter that contains the id of the category ). Each of these children would load details for the sub-category it is responsible for.

Some pseudo code below:

<Route ... component={TopLevelContainer} />

@asyncConnect([{
  promise: ({ store: { dispatch, getState }, params: { categoryId } }) => {
    const promises = [];
    const state = getState();

    if (!categoryActions.isLoaded(state)) {
      promises.push(categoryActions.loadCategory(categoryId));
    }

    .... 
    return Promises.all(promises);
}])
export default class TopLevelContainer extends Component { ....
    someChildContext stuff ...
    ....
    render() {
        return (
            <div>
                <ChildContainer />
                <ChildContainer />
            </div>  
        );
    }
@asyncConnect([{
  promise: ({ store: { dispatch, getState }, params: { categoryId } }) => {
    const promises = [];
    const state = getState();

   // why is this code not running ? Am I forced to keep this on the parent? 

    // COMMENT BELOW
    if (!subCategoryActions.isLoaded(state, categoryId, 'someSubcategory')) {
      promises.push(subCategoryActions.loadCategory(categoryId, 'someSubcategory'));
    }

    .... 
    return Promises.all(promises);
}])
export default class ChildContainer extends Component { ....

So .. I know it's a bit of code, but I was using the repo that this was forked from in hopes that maybe this would work here. And yeah .. am I doing something wrong? Is this a feature that's implemented and support and I'm not doing something correctly? I also found this on the older repo but I'm not sure it's what I need.

Also, regarding the // COMMENT BELOW part, is it somehow possible to get the context in the async connect? Or is that a more general thing?

Any help would be greatly appreciated,
Thank you very much!

I had a weird bug.

Just for sharing my experience. Maybe a solution to remove race condition bug.

Situation

The bug makes my app to stop getting END_GLOBAL_LOAD, so does changing route too.

Investigation

  1. I have found that END_GLOBAL_LOAD is not responding when I start new START_GLOBAL_LOAD before previous END_GLOBAL_LOAD being called.
  2. After this situation occurs mapSeries > iterateOverResults function in helpers/utils won't be called too.
  3. I have digged into _promise2.default, found that Promise is babel-runtime's own implementation.

Solution

I have replaced babel-runtime's Promise to Bluebird, and bug disappeared too.

Add below codes to webpack entry.

const Bluebird = require('bluebird');
Bluebird.config({ warnings: false });
Bluebird.onPossiblyUnhandledRejection(e => {
  throw e;
});
require('babel-runtime/core-js/promise').default = Bluebird;

redirect via router

Given @asyncConnect loads some sort of resource to load in redux... what happens if API returns 404? Need to redirect to another page.

Where to initialise state using async connect

I'm struggling to find a way to use react states which are synced up to the value of async connect calls. In react we shouldn't mutate this.props nor mutate state in render. I have a button that makes an async request and toggles between two states, and should be in either of these states initially based on an async connect call.

In the callback of the button click async requests I use setState to change the state, but Its unclear to me where I can set the state initially before render and after the async connect request has been called. ComponentDidMount doesn't seem to do the job.

Where can I perform a callback on async completion to set the react state, or set the react state after the async has completed and before the component render has been called

Thanks!

[REAME] duplicate ReduxAsyncConnect import variable

n the README on this repo there is a duplicate of an import variable:

import { Router, browserHistory } from 'react-router';
// BELOW is the duplicate of ReduxAsyncConnect
import { ReduxAsyncConnect, asyncConnect, reducer as reduxAsyncConnect } from 'redux-connect' 
import React from 'react'
import { render } from 'react-dom'
import { createStore, combineReducers } from 'redux';   

Possible to dispatch action?

Using redux-connect, my app is calling an API to get a list of companies before rendering a component on the server side:

@asyncConnect([
  {
    key: 'companies',
    promise: ({ params, helpers }) => {
      return helpers.client.get('/companies').then(res => res.companies)
    }
  }
])
class Home extends React.Component
...

This works great but in my app, users can load more companies. When they do, app will call an API and concatenate the result to companies.items state in the store. with redux-connect, the companies.items is empty when the app loads. Instead, reduxAsyncConnect.companies has the companies. Hence all my actions for companies are now useless.

I think possible solutions are:

  1. Make a mapping between reduxAsyncConnect states and my app's states and rehydrate accordingly on the client side when the app first loads
  2. Dispatch action before the component renders on server side and wait for the action to complete, rather than calling API explicitly.

Which one is more viable?

Serialize?

In the README.md, there is a snippet example which use serialize(store.getState()).
What is that? I am certainly missing something here, but it's undefined to me.

Before using redux-connect I was using JSON.stringify to serialize my state object into a string to be able to pass it to html. But now, JSON.stringify block my server when there is an async action going on.

Any help? 😔

add react-router middleware

The README.md gives an example of how you can use this with applyRouterMiddleware. Would it make more sense to provide your own middleware instead? Might look something like:

const useReduxAsyncConnect = (helpers, filters) => ({
  renderRouterContext: (child, props) => (
    <ReduxAsyncConnect helpers={helpers} filters={filters} {...props}>{child}</ReduxAsyncConnect>
  )
})

and would be used like

const component = (
  <Router
    render={(props) => applyRouterMiddleware(useReduxConnect(helpers, filters), useScroll())}
    history={history}
    routes={getRoutes(store)}
  />
);

Async connect gets fired before componentWillUnmount

Hello.
I have a pretty simple set up, a simple component, which dispatches a reset function in componentWillUnmount and a link, which routes on another page, where asyncConnect loads some data asynchronously. However, I have noticed, that an action in the next pages' asyncConnect gets dispached before the reset in the componentWillUnmount.

My question is - how can I dispatch some actions in the componentWillUnmount before asyncConnect's promise is fired?

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.