Use useHistory and useLocation hooks

This commit is contained in:
Przemek Wiech 2021-11-04 21:09:32 +01:00
parent 6e8b6c7b9e
commit 536d9d4210
5 changed files with 36 additions and 46 deletions

View File

@ -11,10 +11,11 @@ import {IndiInfo} from 'topola';
import {Intro} from './intro';
import {Loader, Message, Portal, Tab} from 'semantic-ui-react';
import {Media} from './util/media';
import {Redirect, Route, RouteComponentProps, Switch} from 'react-router-dom';
import {Redirect, Route, Switch} from 'react-router-dom';
import {TopBar} from './menu/top_bar';
import {TopolaData} from './util/gedcom_util';
import {useEffect, useState} from 'react';
import {useHistory, useLocation} from 'react-router';
import {
Chart,
ChartType,
@ -104,7 +105,7 @@ interface Arguments {
selection?: IndiInfo;
chartType: ChartType;
standalone: boolean;
freezeAnimation?: boolean;
freezeAnimation: boolean;
showSidePanel: boolean;
config: Config;
}
@ -171,20 +172,7 @@ function getArguments(location: H.Location<any>): Arguments {
};
}
/**
* Returs true if the changes object has values that are different than those
* in state.
*/
function hasUpdatedValues<T>(state: T, changes: Partial<T> | undefined) {
if (!changes) {
return false;
}
return Object.entries(changes).some(
([key, value]) => value !== undefined && state[key] !== value,
);
}
export function App(props: RouteComponentProps) {
export function App() {
/** State of the application. */
const [state, setState] = useState<AppState>(AppState.INITIAL);
/** Loaded data. */
@ -208,6 +196,8 @@ export function App(props: RouteComponentProps) {
const [config, setConfig] = useState(DEFALUT_CONFIG);
const intl = useIntl();
const history = useHistory();
const location = useLocation();
/** Sets the state with a new individual selection and chart type. */
function updateDisplay(newSelection: IndiInfo) {
@ -296,17 +286,17 @@ export function App(props: RouteComponentProps) {
useEffect(() => {
(async () => {
if (props.location.pathname !== '/view') {
if (location.pathname !== '/view') {
if (state !== AppState.INITIAL) {
setState(AppState.INITIAL);
}
return;
}
const args = getArguments(props.location);
const args = getArguments(location);
if (!args.sourceSpec) {
props.history.replace({pathname: '/'});
history.replace({pathname: '/'});
return;
}
@ -321,6 +311,7 @@ export function App(props: RouteComponentProps) {
setSelection(args.selection);
setStandalone(args.standalone);
setChartType(args.chartType);
setFreezeAnimation(args.freezeAnimation);
setConfig(args.config);
try {
const data = await loadData(args.sourceSpec, args.selection);
@ -371,13 +362,12 @@ export function App(props: RouteComponentProps) {
});
function updateUrl(args: queryString.ParsedQuery<any>) {
const location = props.location;
const search = queryString.parse(location.search);
for (const key in args) {
search[key] = args[key];
}
location.search = queryString.stringify(search);
props.history.push(location);
history.push(location);
}
/**
@ -516,15 +506,14 @@ export function App(props: RouteComponentProps) {
return (
<>
<Route
render={(props: RouteComponentProps) => (
render={() => (
<TopBar
{...props}
data={data?.chartData}
allowAllRelativesChart={
sourceSpec?.source !== DataSourceEnum.WIKITREE
}
showingChart={
props.history.location.pathname === '/view' &&
history.location.pathname === '/view' &&
(state === AppState.SHOWING_CHART ||
state === AppState.LOADING_MORE)
}

View File

@ -5,10 +5,10 @@ import {IndiInfo, JsonGedcomData} from 'topola';
import {Link} from 'react-router-dom';
import {Media} from '../util/media';
import {MenuType} from './menu_item';
import {RouteComponentProps} from 'react-router-dom';
import {SearchBar} from './search';
import {UploadMenu} from './upload_menu';
import {UrlMenu} from './url_menu';
import {useHistory, useLocation} from 'react-router';
import {WikiTreeLoginMenu, WikiTreeMenu} from './wikitree_menu';
enum ScreenSize {
@ -37,14 +37,16 @@ interface Props {
showWikiTreeMenus: boolean;
}
export function TopBar(props: RouteComponentProps & Props) {
export function TopBar(props: Props) {
const history = useHistory();
const location = useLocation();
function changeView(view: string) {
const location = props.location;
const search = queryString.parse(location.search);
if (search.view !== view) {
search.view = view;
location.search = queryString.stringify(search);
props.history.push(location);
history.push(location);
}
}

View File

@ -4,8 +4,8 @@ import {analyticsEvent} from '../util/analytics';
import {Dropdown, Icon, Menu} from 'semantic-ui-react';
import {FormattedMessage} from 'react-intl';
import {MenuType} from './menu_item';
import {RouteComponentProps} from 'react-router-dom';
import {SyntheticEvent} from 'react';
import {useHistory, useLocation} from 'react-router';
function loadFileAsText(file: File): Promise<string> {
return new Promise((resolve) => {
@ -27,7 +27,10 @@ interface Props {
}
/** Displays and handles the "Open file" menu. */
export function UploadMenu(props: RouteComponentProps & Props) {
export function UploadMenu(props: Props) {
const history = useHistory();
const location = useLocation();
async function handleUpload(event: SyntheticEvent<HTMLInputElement>) {
const files = (event.target as HTMLInputElement).files;
if (!files || !files.length) {
@ -68,9 +71,8 @@ export function UploadMenu(props: RouteComponentProps & Props) {
// Use history.replace() when reuploading the same file and history.push() when loading
// a new file.
const search = queryString.parse(props.location.search);
const historyPush =
search.file === hash ? props.history.replace : props.history.push;
const search = queryString.parse(location.search);
const historyPush = search.file === hash ? history.replace : history.push;
historyPush({
pathname: '/view',

View File

@ -3,18 +3,19 @@ import {analyticsEvent} from '../util/analytics';
import {Button, Form, Header, Icon, Input, Modal} from 'semantic-ui-react';
import {FormattedMessage} from 'react-intl';
import {MenuItem, MenuType} from './menu_item';
import {RouteComponentProps} from 'react-router-dom';
import {useEffect, useRef, useState} from 'react';
import {useHistory} from 'react-router';
interface Props {
menuType: MenuType;
}
/** Displays and handles the "Open URL" menu. */
export function UrlMenu(props: RouteComponentProps & Props) {
export function UrlMenu(props: Props) {
const [dialogOpen, setDialogOpen] = useState(false);
const [url, setUrl] = useState('');
const inputRef = useRef<Input>(null);
const history = useHistory();
useEffect(() => {
if (dialogOpen) {
@ -28,7 +29,7 @@ export function UrlMenu(props: RouteComponentProps & Props) {
setDialogOpen(false);
if (url) {
analyticsEvent('url_selected');
props.history.push({
history.push({
pathname: '/view',
search: queryString.stringify({url}),
});

View File

@ -5,24 +5,20 @@ import {Button, Form, Header, Input, Modal} from 'semantic-ui-react';
import {FormattedMessage, useIntl} from 'react-intl';
import {getLoggedInUserName} from '../datasource/wikitree';
import {MenuItem, MenuType} from './menu_item';
import {RouteComponentProps} from 'react-router-dom';
import {useEffect, useRef, useState} from 'react';
enum WikiTreeLoginState {
UNKNOWN,
NOT_LOGGED_IN,
LOGGED_IN,
}
import {useHistory, useLocation} from 'react-router';
interface Props {
menuType: MenuType;
}
/** Displays and handles the "Select WikiTree ID" menu. */
export function WikiTreeMenu(props: RouteComponentProps & Props) {
export function WikiTreeMenu(props: Props) {
const [dialogOpen, setDialogOpen] = useState(false);
const [wikiTreeId, setWikiTreeId] = useState('');
const inputRef = useRef<Input>(null);
const history = useHistory();
const location = useLocation();
useEffect(() => {
if (dialogOpen) {
@ -38,10 +34,10 @@ export function WikiTreeMenu(props: RouteComponentProps & Props) {
return;
}
analyticsEvent('wikitree_id_selected');
const search = queryString.parse(props.location.search);
const search = queryString.parse(location.search);
const standalone =
search.standalone !== undefined ? search.standalone : true;
props.history.push({
history.push({
pathname: '/view',
search: queryString.stringify({
indi: wikiTreeId,