git.fiddlerwoaroof.com
Raw Blame History
import * as R from 'ramda';
import React, {useReducer, useEffect} from 'react';
import PropTypes from 'prop-types';

const RouteContext = React.createContext(null);
const ActionDispatcherContext = React.createContext(null);

function RouteProvider({children, actionDispatcher, _window}) {

  const [route, updateRoute] = useReducer((state, action) =>
      R.omit(['type'], R.assoc('routeName', action.type, action))
    , {});

  useEffect(() => {
    return actionDispatcher.addActionListener(action => updateRoute(action));
  });

  useEffect(() => {
    actionDispatcher.receiveLocation(_window.location);
  });

  return (<ActionDispatcherContext.Provider value={actionDispatcher}>
    <RouteContext.Provider value={route}>
      {children}
    </RouteContext.Provider>
  </ActionDispatcherContext.Provider>);
}

RouteProvider.defaultProps = {
  _window: window ? window : null
};

RouteProvider.propTypes = {
  _window: PropTypes.object,
  children: PropTypes.oneOfType([
    PropTypes.arrayOf(PropTypes.node),
    PropTypes.node
  ]),
  actionDispatcher: PropTypes.object
};

function getDisplayName(WrappedComponent) {
  return WrappedComponent.displayName || WrappedComponent.name || 'Component';
}

function withRoute(Component) {
  const routeAdded = ({children, ...restProps}) => {
    return (<RouteContext.Consumer>
        {route =>
          <Component {...{...restProps, route}}>{children}</Component>
        }
      </RouteContext.Consumer>
    )
  };
  routeAdded.displayName = `withRoute(${getDisplayName(Component)})`
  routeAdded.propTypes = {
    children: PropTypes.oneOfType([
      PropTypes.arrayOf(PropTypes.node),
      PropTypes.node
    ])
  };
  return routeAdded;
}

function RouteLink({route, params, children, ...props}) {
  const action = {
    type: route, ...params
  };
  return <ActionDispatcherContext.Consumer>{
    dispatcher => {
      const url = dispatcher.pathForAction(action);
      return <a
        onClick={(ev) => {
          ev.preventDefault();
          dispatcher.receiveAction(action, true);
        }}
        href={url} {...props}>{children}</a>
    }
  }
  </ActionDispatcherContext.Consumer>;
}

RouteLink.propTypes = {
  route: PropTypes.string,
  params: PropTypes.object,
  children: PropTypes.oneOfType([
    PropTypes.arrayOf(PropTypes.node),
    PropTypes.node
  ]),
};

export {RouteProvider, withRoute, RouteLink};