{"id":15070020,"url":"https://github.com/pop-code/redux-async-load","last_synced_at":"2025-10-05T06:31:33.959Z","repository":{"id":57350259,"uuid":"82707663","full_name":"Pop-Code/redux-async-load","owner":"Pop-Code","description":"This module merges the logic of loading data on the client side and the server side.","archived":true,"fork":false,"pushed_at":"2017-08-07T01:46:55.000Z","size":22,"stargazers_count":5,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-01-15T14:22:22.513Z","etag":null,"topics":["dispatch","react","react-js","react-redux","redux","redux-async"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/redux-async-load","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Pop-Code.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-02-21T17:27:37.000Z","updated_at":"2023-01-28T16:05:59.000Z","dependencies_parsed_at":"2022-08-28T17:52:40.698Z","dependency_job_id":null,"html_url":"https://github.com/Pop-Code/redux-async-load","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Pop-Code%2Fredux-async-load","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Pop-Code%2Fredux-async-load/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Pop-Code%2Fredux-async-load/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Pop-Code%2Fredux-async-load/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Pop-Code","download_url":"https://codeload.github.com/Pop-Code/redux-async-load/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":235370461,"owners_count":18979093,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dispatch","react","react-js","react-redux","redux","redux-async"],"created_at":"2024-09-25T01:46:31.801Z","updated_at":"2025-10-05T06:31:33.570Z","avatar_url":"https://github.com/Pop-Code.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# redux-async-load\n\nThis module merges the logic of loading data on the client side and the server side.\n\nPurpose\n-\n\nWith react, we use to do async things (generally loading data etc...) in component life cycles.\nWhen using redux with react, your connected components will generally be instances of \"PureComponent\", that means they are stateless.\n(read more about react pure component and why this concept was introduced) \nThis difference is a big thing, With redux, you store your data into the state (that is connected to props), not into component state.\n\nWhen using SSR (Server side rendering), components do not have life cycles, the \"render\" only occurs once (renderToString).\nTo have the state totally hydrated, we need to render multiple times.\nDuring each render, components might dispatch actions to the store and the redux state might change, changing the result of the next render.\n\nOn client side, this is not a problem, when the state changes, connected components receives new props and will update.\n\nSo we need a module that can re-render until the state is ready without implementing a specific server logic.\nThis module use the \"componentWillMount\" event on both env to dispatch loading actions, and trigger events to mark async components as loaded in the state.\nOn server side, each time the store receives a event like this, he will re render, until all the components are ready, then send the response to browser.\n\n##### SSR workflow example:\n\n1. Server is rendering\n2. Component \"Foo\" dispatch an action to tell he is loading, and start doing jobs, result is going to the state to be connected later.\n3. Data are loaded, \"Foo\" dispatch an action to tell he is ready\n4. Store receive this action and examine the list of remaining actions\n    - The list of remaining actions appears to be empty, that mean the state should have all required data to render \"foo\", do a final render.\n    - OR\n    - The list of remaining actions is not empty, \"foo\" or an other component is loading =\u003e we wait for next state notification then re render =\u003e (Go to 1.)\n\nThis allow us to have a deep three rendering on server side.\n\n## Use case\nWith the new react router v4, routes are loaded in components, if you have some routes inside a component that is deep in your three,\nreact won't see this route during the renderToString.\nreact-async-load is a tool to build you state data using re-render on server side.\n\n\n## Install\n\n```ssh\nnpm install redux-async-load --save\n```\n\n## Api\n\n### renderAsync(store: Object, render: () =\u003e string, stateKey: string = 'asyncLoad'):Promise\\\u003cstring\u003e\nFor Server side only\n\n##### Args\n- store: Object \n  - the redux store implementing the reducer from this module and the rest of your data\n- render: Function \n  - no args\n  - return string\n  - This method will be invoked for each render on server side, you must return the html of your app using renderToString from react-dom\n- stateKey: string \n    - default: 'asyncLoad'\n    - This is the key of the reducer in your state\n##### Returns\nA Promise with the final html string of the application\n\n### stateSelector:(key = 'asyncLoad', state: {Object}) =\u003e asyncState: {[loadId]: {loading: boolean}}\nstateSelector is used to find the asyncState inside the redux state.\n- key: string\n    - The key of the asyncState in the redux state (default = 'asyncLoad')\n- state: Object \n    - The redux state\n\n### isReady:(asyncState: {[loadId]: {loading: boolean}}) =\u003e boolean\nisReady will check the asyncState to know if all components are loaded\n- asyncState: Object\n    - This is the state of async components. Use the stateSelector to get it from the redux state\n- return boolean true if all components are loaded  \n\n## Action creator\n### asyncSetStatus:(loadId, {loading: boolean})\n- loadId: string\n    - This is unique identifier of the operation in the asyncState\n- return an action\n\n## Components\n\n### \\\u003cReduxLoader /\u003e\n#### props\n- loadId: string The Id of the connected element\n- shouldLoad:(props) =\u003e boolean \n    - props: Object The props passed to the component\n    - If this method return true, and if the component is not marked as loaded in state, the component will call the load function passed in props\n- load: (props) =\u003e Promise\\\u003cany\u003e | null\n    - props Object The props passed to the component\n    - If this method returns a promise once load has been done, the loaded status of the component will be set automatically to false.\n      If not, you will have to dispatch the action asyncSetStatus\n      Normally, It should returns the result of an action\n- shouldReload: (newProps, oldProps) =\u003e boolean\n    - newProps: Object The new props of the component\n    - oldProps: Object The old props of the component\n    - If this method return true, the component will call the load function passed in props\n- render: (props) =\u003e React.Element If this property is present, It'll be used as the default render, and must return a single valid component. \n    - props: Object The props passed to the component\n\n## Usage\n\n\n### Add the reducer to your redux store (store.js)\n```JSX\nimport {createStore, combineReducers}  from 'redux'\nimport {reducer as asyncLoad} from 'redux-async-load'\n\nexport default createStore(combineReducers({\n   asyncLoad, // \u003c-- add the reducer to your reducers \n   //[your reducers]\n}), yourInitialState)\n```\n\n### Create an async component\n```JSX\nimport React, {Component} from 'react'\nimport {connect} from 'react-redux'\nimport {ReduxLoader} from 'redux-async-load'\n\n//this is a redux action creator to load data that will use index to load the user in state.\nimport {myLoadAction} from './action'\n\nconst User = props =\u003e \u003cp\u003e{props.user \u0026\u0026 props.user.name}\u003c/p\u003e\n\n/*\n * Props that represent loading logic are implemented in the ReduxLoader\n * If your props are dynamics and represent data or identifiers, you should implement them directly on the AsyncUser\n * Model: AsyncUser(props) =\u003e ReduxLoader({...logicProps, props})\n */\nconst AsyncUser = props =\u003e \u003cReduxLoader \n    {/* Do load only if we do not have data */}\n    shouldLoad={props =\u003e !props.data}\n    \n    {/* This tells the component how to load your data */}\n    {/* This is not invoked if you pass an action creator named \"load\" to connect because it will replace this props */}\n    load={props =\u003e myLoadAction(props.userId)}\n    \n    {/* The component receives props, tell him if he must reload data when userId change */}\n    shouldReload={(props, oldProps) =\u003e (props.userId !== oldProps.userId)}\n    \n    {/* This is the render method, use it to an altenrative way to render children */}\n    {/* If the component contains chilren, they will be present in props.children */}\n    render={props =\u003e \u003cUser {...props} /\u003e}\n\u003e\n    {/* OR */}\n    {/* This is the normal way of rendering an element */}\n    {/* \u003cUser /\u003e will be cloned with all props */}\n    {/* If the prop render is present, children will be passed as props */}\n    \u003cUser /\u003e\n\u003c/ReduxLoader\u003e\n\n\n//connect our component to the state\nexport default connect((state, {userId}) =\u003e {\n    return {\n        //loadId is required by Redux loader to set the loading flag in redux async state\n        loadId: userId\n        //We suppose your load action and his reducer will hydrate this part of the state with data (used buy AsyncUser)\n        data: state.user[userId]\n    }\n}, ({load}))(AsyncUser)\n\n```\n\n### Use the new async component where you want (app.js)\n```jsx\nexport default props =\u003e (\n    \u003cdiv\u003e\n        \u003cp\u003eThis component was preloaded on server, see the source code...\u003c/p\u003e\n        \u003cAsyncUser userId={12345}/\u003e\n    \u003c/div\u003e\n)\n```\n\n### Render on server side (server.js)\n```jsx\nimport {renderToString} from 'react-dom'\nimport {renderAsync} from 'redux-async-load'\nimport {Provider} from 'react-redux'\nimport store from './store'\nimport App from './app'\nimport Html from './html' // \u003c= your html template\nimport express from 'express'\n\nconst app = new express()\n\napp.use((req, res, next) =\u003e {\n    \n    //Your other jobs\n    \n    //render async will subscribe to the store, and knows when the data are loaded\n    renderAsync(store, () =\u003e renderToString(\u003cProvider store={store}\u003e\u003cApp /\u003e\u003c/Provider\u003e))\n        //Render the generated html as you do normally\n        .then(appHtml =\u003e {\n            // Build your html structure with your favorite tool (like Helmet)\n            const html = \u003cHtml content={appHtml} /\u003e \n            res.send('\u003c!doctype html\u003e\\n' + html)\n        })\n        .catch(next)  \n})\n```\n\n### Render on client side as usual (client.js)\n```jsx\nimport {render} from 'react-dom'\nimport {Provider} from 'react-redux'\nimport store from './store'\nimport App from './app'\n\nrender(\u003cProvider store={store}\u003e\u003cApp /\u003e\u003c/Provider\u003e, document.getElementById('app-root'))\n```\n\n#### Todo\n\n- add examples\n- add tests\n\n#### Disclamer\n\nThis is clearly experimental, and maybe removed if a better solution is found.\nYou should take all precautions to prevent huge loading.\nIf the schema of your app is very deep, you might want to preload data in a high order component that will prevent re rendering one more time\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpop-code%2Fredux-async-load","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpop-code%2Fredux-async-load","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpop-code%2Fredux-async-load/lists"}