0.7.1 • Published 7 years ago

react-router-hook v0.7.1

Weekly downloads
4
License
MIT
Repository
github
Last release
7 years ago

react-router-hook

Universal data fetching and lifecycle management for react-router@3 with multiple components. Inspired by redial, react-router-redial and async props.

CircleCI dependency status

Installation

npm install --save react-router-hook

Usage

import { browserHistory, Router, applyRouterMiddleware } from 'react-router';
import { useRouterHook, routerHooks } from 'react-router-hook';

const locals = {
  dispatch: store.dispatch, // redux store and dispatch, you can use any locals
  getState: store.getState,
};

const onAborted = () => {
  console.info('aborted');
};
const onCompleted = () => {
  console.info('completed');
};
const onError = (error) => {
  console.error(error);
};

const routerHookMiddleware = useRouterHook({
  locals,
  routerWillEnterHooks: ['fetch'],
  routerDidEnterHooks: ['defer', 'done'],
  onAborted,
  onStarted,
  onCompleted,
  onError,
});

ReactDOM.render((
  <Router
    history={browserHistory}
    render={applyRouterMiddleware(routerHookMiddleware))}
  >
    <Route path="/" component={App}>
      <Route path="users" components={{main: Users, footer: lazy({ style: { height: 500 } })(UserFooter)}} />
    </Route>
  </Router>
), node)
class App extends React.Component {
  render() {
    // the matched child route components become props in the parent
    return (
      <div>
        <div className="Main">
          {/* this will either be <Groups> or <Users> */}
          {this.props.main}
        </div>
        <div className="Footer">
          {/* this will either be <GroupsSidebar> or <UsersSidebar> */}
          {this.props.footer}
        </div>
      </div>
    )
  }
}

@routerHooks({
  fetch: async () => {
    await fetchData();
  },
  defer: async () => {
    await fetchDeferredData();
  },
})
class Users extends React.Component {
  render() {
    return (
      <div>
        {/* if at "/users/123" this will be <Profile> */}
        {/* UsersSidebar will also get <Profile> as this.props.children.
            You can pick where it renders */}
        {this.props.children}
      </div>
    )
  }
}

@routerHook({
  fetch: async () => {
    await fetchData();
  },
  defer: async () => {
    await fetchDeferredData();
  },
})
class UserFooter extends React.Component {
  render() {
    return (
      <div>
        UserFooter
      </div>
    )
  }
}

On server side

import { match } from 'react-router';
import { triggerHooksOnServer } from 'react-router-hook';
// Other imports

import routes from './routes';

app.get('*', (req, res) => {
  // create redux store (Optional);
  const store = createStore();

  match({
    history,
    routes,
    location: req.url,
  }, (err, redirectLocation, renderProps) => {
    if (err) {
      // Error Handler
    }
    const locals = {
      dispatch: store.dispatch,
      getState: store.getState,
    };
    triggerHooksOnServer(
      renderProps,
      ['fetch', 'defer'],
      {
        dispatch,
        getState,
      },
      // If onComponentError is null, callback will be immediately called with the error
      onComponentError: (err) => {
        console.error(err.Component, err.error);
      },
      // triggerHooksOnServer() will return a Promise if there is no callback
      (err) => {
        if (err) {
          res.status(500).end();
          return;
        }
        const body = ReactDOMServer.renderToString(
          <Provider store={store}>
            <RouterContext {...renderProps} />
          </Provider>,
        );
        res.send(`<html><head></head><body>${body}</body>`);
      },
  });
});
triggerHooksOnServer

Monitoring router status

@routerHook({
  fetch: async () => {
    await fetchData();
  },
  defer: async () => {
    await fetchDeferredData();
  },
})
class SomeComponent extends React.Component{

  static contextTypes = {
    routerHookContext: routerHookContextShape,
  };

  constructor(props, context) {
    super(props, context);
    this.state = {
      routerLoading: false,
    };
  }

  componentWillMount() {
    if (this.context.routerHookContext) {
      this.removeListener = this.context.routerHookContext.addLoadingListener((loading, info) => {
        const { total, init, defer, done } = info;
        console.info(loading, total, init, defer, done);
        this.setState({
          routerLoading: loading,
        });
      });
    }
  }

  componentWillUnmount() {
    if (this.removeListener) {
      this.removeListener();
    }
  }

  render() {
    return (
      <div>
        is loading: {this.state.routerLoading}
      </div>
    );
  }
}
0.7.1

7 years ago

2.0.0-beta.13

7 years ago

2.0.0-beta.12

7 years ago

2.0.0-beta.11

7 years ago

2.0.0-beta.10

8 years ago

2.0.0-beta.9

8 years ago

2.0.0-beta.8

8 years ago

2.0.0-beta.7

8 years ago

2.0.0-beta.6

8 years ago

2.0.0-beta.5

8 years ago

2.0.0-beta.4

8 years ago

2.0.0-beta.3

8 years ago

2.0.0-beta.2

8 years ago

2.0.0-beta.1

8 years ago

0.7.0

8 years ago

0.6.2

8 years ago

0.6.1

9 years ago

0.6.0

9 years ago

0.5.1

9 years ago

0.5.0

9 years ago

0.4.1

9 years ago

0.4.0

9 years ago

0.3.0

9 years ago

0.2.1

9 years ago

0.2.0

9 years ago

0.1.1

9 years ago

0.1.0

9 years ago

0.0.19

9 years ago

0.0.18

10 years ago

0.0.17

10 years ago

0.0.16

10 years ago

0.0.15

10 years ago

0.0.14

10 years ago

0.0.13

10 years ago

0.0.12

10 years ago

0.0.11

10 years ago

0.0.10

10 years ago

0.0.9

10 years ago

0.0.8

10 years ago

0.0.7

10 years ago

0.0.6

10 years ago

0.0.5

10 years ago

0.0.4

10 years ago

0.0.3

10 years ago

0.0.2

10 years ago

0.0.1

10 years ago