Skip to content

Commit

Permalink
Merge pull request #876 from 4Catalyzer/shrink
Browse files Browse the repository at this point in the history
consolidate code and move towards removing react-redux
  • Loading branch information
jquense authored Jan 4, 2022
2 parents f020ef2 + a6ff211 commit 124e867
Show file tree
Hide file tree
Showing 11 changed files with 200 additions and 226 deletions.
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ module.exports = {
requireConfigFile: false,
},
rules: {
'react/prop-types': 'off',
'prettier/prettier': 'error',
},
};
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
"dependencies": {
"@babel/runtime": "^7.16.5",
"@restart/context": "^2.1.4",
"@restart/hooks": "^0.4.5",
"@types/react": ">=17.0.37",
"dequal": "^2.0.2",
"farce": "^0.4.5",
Expand Down
134 changes: 0 additions & 134 deletions src/BaseLink.js

This file was deleted.

109 changes: 105 additions & 4 deletions src/Link.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,108 @@
import BaseLink from './BaseLink';
import withRouter from './withRouter';
import useEventCallback from '@restart/hooks/useEventCallback';
import React from 'react';
import warning from 'tiny-warning';

const Link = withRouter(BaseLink);
Link.displayName = 'Link';
import useRouter from './useRouter';

function Link({
as: Component = 'a',
to,
activeClassName,
activeStyle,
activePropName,
match: propsMatch,
router: propsRouter,
exact = false,
onClick,
target,
...props
}) {
const { router, match } = useRouter() || {
match: propsMatch,
router: propsRouter,
};

const handleClick = useEventCallback((event) => {
if (onClick) {
onClick(event);
}

// Don't do anything if the user's onClick handler prevented default.
// Otherwise, let the browser handle the link with the computed href if the
// event wasn't an unmodified left click, or if the link has a target other
// than _self.
if (
event.defaultPrevented ||
event.metaKey ||
event.altKey ||
event.ctrlKey ||
event.shiftKey ||
event.button !== 0 ||
(target && target !== '_self')
) {
return;
}

event.preventDefault();

// FIXME: When clicking on a link to the same location in the browser, the
// actual becomes a replace rather than a push. We may want the same
// handling – perhaps implemented in the Farce protocol.
router.push(to);
});

if (__DEV__ && typeof Component !== 'function') {
for (const wrongPropName of ['component', 'Component']) {
const wrongPropValue = props[wrongPropName];
if (!wrongPropValue) {
continue;
}

warning(
false,
'Link to %s with `%s` prop `%s` has an element type that is not a component. The expected prop for the link component is `as`.',
JSON.stringify(to),
wrongPropName,
wrongPropValue.displayName || wrongPropValue.name || 'UNKNOWN',
);
}
}

const href = router.createHref(to);
const childrenIsFunction = typeof props.children === 'function';

if (childrenIsFunction || activeClassName || activeStyle || activePropName) {
const toLocation = router.createLocation(to);
const active = router.isActive(match, toLocation, { exact });

if (childrenIsFunction) {
return props.children({ href, active, onClick: handleClick });
}

if (active) {
if (activeClassName) {
props.className = props.className
? `${props.className} ${activeClassName}`
: activeClassName;
}

if (activeStyle) {
props.style = { ...props.style, ...activeStyle };
}
}

if (activePropName) {
props[activePropName] = active;
}
}

return (
<Component
{...props}
href={href}
onClick={handleClick} // This overrides props.onClick.
/>
);
}

export default Link;
37 changes: 10 additions & 27 deletions src/createBaseRouter.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import mapContextToProps from '@restart/context/mapContextToProps';
import { dequal } from 'dequal';
import PropTypes from 'prop-types';
import React from 'react';
import { ReactReduxContext } from 'react-redux';
import warning from 'tiny-warning';

import ActionTypes from './ActionTypes';
import RouterContext from './RouterContext';
import StaticContainer from './StaticContainer';
import createRender from './createRender';
Expand All @@ -21,18 +19,6 @@ export default function createBaseRouter({
renderError,
}),
}) {
const propTypes = {
store: PropTypes.object.isRequired,
match: PropTypes.object.isRequired,
resolvedMatch: PropTypes.object.isRequired,
matchContext: PropTypes.any,
resolver: PropTypes.shape({
resolveElements: PropTypes.func.isRequired,
}).isRequired,
onResolveMatch: PropTypes.func.isRequired,
initialRenderArgs: PropTypes.object,
};

class BaseRouter extends React.Component {
constructor(props) {
super(props);
Expand All @@ -59,6 +45,13 @@ export default function createBaseRouter({

this.lastIteration = 0;
this.pendingResolvedMatch = false;

this.dispatchMatch = (pendingMatch) => {
store.dispatch({
type: ActionTypes.RESOLVE_MATCH,
payload: pendingMatch,
});
};
}

// We use componentDidMount and componentDidUpdate to resolve the match if
Expand Down Expand Up @@ -164,7 +157,7 @@ export default function createBaseRouter({
// the same time as all of the links and other components
// connected to the router state.
this.pendingResolvedMatch = false;
this.props.onResolveMatch(pendingMatch);
this.dispatchMatch(pendingMatch);
}
}
} catch (e) {
Expand Down Expand Up @@ -200,15 +193,5 @@ export default function createBaseRouter({
}
}

BaseRouter.propTypes = propTypes;

// FIXME: For some reason, using contextType doesn't work here.
return mapContextToProps(
{
consumers: ReactReduxContext,
mapToProps: ({ store }) => ({ store }),
displayName: 'withStore(BaseRouter)',
},
BaseRouter,
);
return BaseRouter;
}
42 changes: 17 additions & 25 deletions src/createConnectedRouter.js
Original file line number Diff line number Diff line change
@@ -1,33 +1,25 @@
import { connect } from 'react-redux';
import React from 'react';
import { shallowEqual, useSelector, useStore } from 'react-redux';

import ActionTypes from './ActionTypes';
import createBaseRouter from './createBaseRouter';

function resolveMatch(match) {
return {
type: ActionTypes.RESOLVE_MATCH,
payload: match,
};
}

export default function createConnectedRouter({
getFound = ({ found }) => found,
...options
}) {
return connect(
(state) => {
const { match, resolvedMatch } = getFound(state);
return { match, resolvedMatch };
},
{
onResolveMatch: resolveMatch,
},
null,
{
// Don't block context propagation from above. The router should seldom
// be unnecessarily rerendering anyway.
pure: false,
getDisplayName: () => 'ConnectedRouter',
},
)(createBaseRouter(options));
const Router = createBaseRouter(options);

const getFoundState = (state) => {
const { match, resolvedMatch } = getFound(state);
return { match, resolvedMatch };
};

function ConnectedRouter(props) {
const store = useStore();
const foundState = useSelector(getFoundState, shallowEqual);

return <Router {...props} {...foundState} store={store} />;
}

return ConnectedRouter;
}
Loading

0 comments on commit 124e867

Please sign in to comment.