aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNoah Loomans <noahloomans@gmail.com>2018-06-28 16:03:46 +0200
committerNoah Loomans <noahloomans@gmail.com>2018-06-28 16:03:46 +0200
commit41620ceb096a4c3d94bb83cf9a56077939d89a2c (patch)
tree8a4bf7ea74d2f16d6d77a5e8cc57c9a9a2fc5c80
parente741808bb640abdd7303be7ba0cf519f68b2977f (diff)
Refactor search
-rw-r--r--src/client/react/App.js17
-rw-r--r--src/client/react/AppRouter.js55
-rw-r--r--src/client/react/components/container/Search.js62
-rw-r--r--src/client/react/store/actions.js2
-rw-r--r--src/client/react/store/reducers.js17
5 files changed, 90 insertions, 63 deletions
diff --git a/src/client/react/App.js b/src/client/react/App.js
index a5a5cbd..522b746 100644
--- a/src/client/react/App.js
+++ b/src/client/react/App.js
@@ -1,17 +1,10 @@
import React from 'react';
import PropTypes from 'prop-types';
-import {
- Router,
- Route,
- Switch,
- Redirect,
-} from 'react-router-dom';
-
import { Provider } from 'react-redux';
+import { Router } from 'react-router-dom';
-import Index from './components/page/Index';
-import User from './components/page/User';
+import AppRouter from './AppRouter';
export default class App extends React.Component {
static propTypes = {
@@ -25,11 +18,7 @@ export default class App extends React.Component {
return (
<Provider store={store}>
<Router history={history}>
- <Switch>
- <Route exact path="/" component={Index} />
- <Route path="/:type/:value" component={User} />
- <Redirect to="/" />
- </Switch>
+ <AppRouter />
</Router>
</Provider>
);
diff --git a/src/client/react/AppRouter.js b/src/client/react/AppRouter.js
new file mode 100644
index 0000000..ae637f0
--- /dev/null
+++ b/src/client/react/AppRouter.js
@@ -0,0 +1,55 @@
+import React from 'react';
+
+import {
+ withRouter,
+ Route,
+ Switch,
+ Redirect,
+} from 'react-router-dom';
+import { connect } from 'react-redux';
+import { PropTypes } from 'prop-types';
+
+import Index from './components/page/Index';
+import User from './components/page/User';
+import { setUser } from './store/actions';
+import { userFromLocation } from './lib/url';
+
+class AppRouter extends React.Component {
+ static propTypes = {
+ user: PropTypes.string,
+ resetUserState: PropTypes.func.isRequired,
+ }
+
+ static defaultProps = {
+ user: null,
+ }
+
+ componentDidMount() {
+ const { user, resetUserState } = this.props;
+
+ resetUserState(user);
+ }
+
+ render() {
+ return (
+ <Switch>
+ <Route exact path="/" component={Index} />
+ <Route path="/:type/:value" component={User} />
+ <Redirect to="/" />
+ </Switch>
+ );
+ }
+}
+
+const mapStateToProps = (state, { location }) => {
+ return {
+ key: location.pathname,
+ user: userFromLocation(location),
+ };
+};
+
+const mapDispatchToProps = dispatch => ({
+ resetUserState: user => dispatch(setUser(user)),
+});
+
+export default withRouter(connect(mapStateToProps, mapDispatchToProps)(AppRouter));
diff --git a/src/client/react/components/container/Search.js b/src/client/react/components/container/Search.js
index 865a0bc..3e599e7 100644
--- a/src/client/react/components/container/Search.js
+++ b/src/client/react/components/container/Search.js
@@ -26,7 +26,8 @@ import { withRouter } from 'react-router-dom';
import SearchIcon from 'react-icons/lib/md/search';
-import { makeSetUser, userFromMatch } from '../../lib/url';
+import { userFromMatch } from '../../lib/url';
+import { setUser as setUserAction } from '../../store/actions';
import users from '../../users';
import Menu from './Menu';
@@ -37,15 +38,12 @@ import './Search.scss';
class Search extends React.Component {
static propTypes = {
- results: PropTypes.arrayOf(PropTypes.string).isRequired,
selectedResult: PropTypes.string,
searchText: PropTypes.string.isRequired,
-
- // react-router
match: PropTypes.object.isRequired,
setUser: PropTypes.func.isRequired,
- // redux
- dispatch: PropTypes.func.isRequired,
+ onInputChange: PropTypes.func.isRequired,
+ changeSelectedResult: PropTypes.func.isRequired,
};
static defaultProps = {
@@ -64,22 +62,6 @@ class Search extends React.Component {
this.onKeyDown = this.onKeyDown.bind(this);
}
- componentDidMount() {
- const { dispatch, match } = this.props;
- const urlUser = userFromMatch(match);
-
- dispatch({ type: 'SEARCH/SET_USER', user: urlUser });
- }
-
- componentWillReceiveProps(nextProps) {
- const { dispatch, match } = this.props;
-
- if (nextProps.match !== match) {
- const urlUser = userFromMatch(nextProps.match);
- dispatch({ type: 'SEARCH/SET_USER', user: urlUser });
- }
- }
-
onFocus() {
this.setState({
hasFocus: true,
@@ -95,40 +77,33 @@ class Search extends React.Component {
onKeyDown(event) {
const {
selectedResult,
- results,
match,
setUser,
- dispatch,
+ changeSelectedResult,
} = this.props;
const urlUser = userFromMatch(match);
- const result = selectedResult || results[0];
switch (event.key) {
case 'ArrowUp':
event.preventDefault();
- dispatch({ type: 'SEARCH/CHANGE_SELECTED_RESULT', relativeChange: -1 });
+ changeSelectedResult(-1);
break;
case 'ArrowDown':
event.preventDefault();
- dispatch({ type: 'SEARCH/CHANGE_SELECTED_RESULT', relativeChange: +1 });
+ changeSelectedResult(+1);
break;
case 'Escape':
event.preventDefault();
- dispatch({ type: 'SEARCH/SET_USER', user: urlUser });
+ setUser(urlUser);
break;
case 'Enter':
event.preventDefault();
- if (result === urlUser) {
- // EDGE CASE: The user is set if the user changes, but it doesn't
- // change if the result is already the one we are viewing.
- // Therefor, we need to dispatch the SET_USER command manually.
- dispatch({ type: 'SEARCH/SET_USER', user: urlUser });
- } else if (result) {
- setUser(result);
+ if (selectedResult) {
+ setUser(selectedResult);
}
break;
@@ -141,7 +116,7 @@ class Search extends React.Component {
const {
searchText,
match,
- dispatch,
+ onInputChange,
} = this.props;
const {
@@ -166,7 +141,7 @@ class Search extends React.Component {
</div>
<input
id="searchInput"
- onChange={event => dispatch({ type: 'SEARCH/INPUT_CHANGE', searchText: event.target.value })}
+ onChange={event => onInputChange(event.target.value)}
onKeyDown={this.onKeyDown}
value={searchText}
placeholder="Zoeken"
@@ -189,9 +164,16 @@ const mapStateToProps = state => ({
selectedResult: state.search.selected,
});
-const mapDispatchToProps = (dispatch, { history }) => ({
- setUser: makeSetUser(history),
- dispatch,
+const mapDispatchToProps = dispatch => ({
+ setUser: user => dispatch(setUserAction(user)),
+ onInputChange: searchText => dispatch({
+ type: 'SEARCH/INPUT_CHANGE',
+ searchText,
+ }),
+ changeSelectedResult: relativeChange => dispatch({
+ type: 'SEARCH/CHANGE_SELECTED_RESULT',
+ relativeChange,
+ }),
});
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Search));
diff --git a/src/client/react/store/actions.js b/src/client/react/store/actions.js
index 46a38ab..e6aa8f6 100644
--- a/src/client/react/store/actions.js
+++ b/src/client/react/store/actions.js
@@ -13,7 +13,7 @@ export function setUser(newUser) {
// Therefor, we need to dispatch the SET_USER command manually.
dispatch({ type: 'SEARCH/SET_USER', user });
} else {
- updatePathname(newUser);
+ updatePathname(newUser || '');
}
};
}
diff --git a/src/client/react/store/reducers.js b/src/client/react/store/reducers.js
index c2ee7e9..3425de0 100644
--- a/src/client/react/store/reducers.js
+++ b/src/client/react/store/reducers.js
@@ -74,30 +74,31 @@ function reducer(state = DEFAULT_STATE, action) {
};
}
- case 'SEARCH/INPUT_CHANGE':
+ case 'SEARCH/INPUT_CHANGE': {
+ const results = getSearchResults(action.searchText);
+
return {
...state,
search: {
- results: getSearchResults(action.searchText),
+ results,
text: action.searchText,
- selected: null,
+ selected: results.length > 0 ? results[0] : null,
},
};
+ }
case 'SEARCH/CHANGE_SELECTED_RESULT': {
const prevSelectedResult = state.search.selected;
const prevSelectedResultIndex = state.search.results.indexOf(prevSelectedResult);
let nextSelectedResultIndex = prevSelectedResultIndex + action.relativeChange;
- if (nextSelectedResultIndex < -1) {
+ if (nextSelectedResultIndex < 0) {
nextSelectedResultIndex = state.search.results.length - 1;
} else if (nextSelectedResultIndex > state.search.results.length - 1) {
- nextSelectedResultIndex = -1;
+ nextSelectedResultIndex = 0;
}
- const nextSelectedResult = nextSelectedResultIndex === -1
- ? null
- : state.search.results[nextSelectedResultIndex];
+ const nextSelectedResult = state.search.results[nextSelectedResultIndex];
return {
...state,