remove more usestates

This commit is contained in:
Michael Fatemi 2021-07-15 14:02:54 -04:00
parent 9bf4f4c568
commit 9f7c40c7df
14 changed files with 77 additions and 266 deletions

View File

@ -1,8 +1,8 @@
import { CSSProperties, lazy, Suspense } from 'react'; import { CSSProperties, lazy, Suspense } from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom'; import { BrowserRouter, Route, Switch } from 'react-router-dom';
import GroupsProvider from '../state/GroupsProvider';
import NotificationsProvider from '../state/Notifications/NotificationsProvider'; import NotificationsProvider from '../state/Notifications/NotificationsProvider';
import { useMe } from './hooks'; import { useMe } from './hooks';
import UseImmutableTest from './UseImmutableTest';
import WheelShare from './WheelShare'; import WheelShare from './WheelShare';
import WheelShareLoggedOut from './WheelShareLoggedOut'; import WheelShareLoggedOut from './WheelShareLoggedOut';
@ -26,30 +26,29 @@ export default function App() {
return ( return (
<NotificationsProvider> <NotificationsProvider>
<GroupsProvider> <div style={{ padding: '1rem', maxWidth: '100vw' }}>
<div style={{ padding: '1rem', maxWidth: '100vw' }}> <UseImmutableTest />
<div style={style}> <div style={style}>
<BrowserRouter> <BrowserRouter>
<Switch> <Switch>
<Route
path="/"
exact
component={user ? WheelShare : WheelShareLoggedOut}
/>
<Suspense fallback={null}>
<Route <Route
path="/" component={Authenticator}
exact path="/auth/:provider/callback"
component={user ? WheelShare : WheelShareLoggedOut}
/> />
<Suspense fallback={null}> <Route path="/carpools/:id" component={CarpoolPage} />
<Route <Route path="/events/:id" component={EventPage} />
component={Authenticator} <Route path="/groups/:id" component={Group} />
path="/auth/:provider/callback" </Suspense>
/> </Switch>
<Route path="/carpools/:id" component={CarpoolPage} /> </BrowserRouter>
<Route path="/events/:id" component={EventPage} />
<Route path="/groups/:id" component={Group} />
</Suspense>
</Switch>
</BrowserRouter>
</div>
</div> </div>
</GroupsProvider> </div>
</NotificationsProvider> </NotificationsProvider>
); );
} }

View File

@ -38,7 +38,7 @@ export default function MemberList() {
cancelCarpoolRequest(carpool.id); cancelCarpoolRequest(carpool.id);
}, [carpool.id, cancelCarpoolRequest]); }, [carpool.id, cancelCarpoolRequest]);
const me = useMe()!; const me = useMe() || { id: 0, name: '' };
const isMember = useMemo(() => { const isMember = useMemo(() => {
return members.some(({ id }) => id === me?.id); return members.some(({ id }) => id === me?.id);

View File

@ -46,7 +46,7 @@ export default function Event({
...(initial || {}), ...(initial || {}),
}); });
const me = useMe()!; const me = useMe() || { id: 0, name: '' };
const [tentativeInvites] = useImmutable<Record<number, boolean>>({}); const [tentativeInvites] = useImmutable<Record<number, boolean>>({});

View File

@ -15,7 +15,7 @@ export default function EventCarpoolCreateButton() {
const [creationStatus, setCreationStatus] = useState<CreationStatus>(null); const [creationStatus, setCreationStatus] = useState<CreationStatus>(null);
const [createdCarpoolId, setCreatedCarpoolId] = useState<null | number>(null); const [createdCarpoolId, setCreatedCarpoolId] = useState<null | number>(null);
const me = useMe()!; const me = useMe() || { id: 0, name: '' };
const myCarpool = useMyCarpool(); const myCarpool = useMyCarpool();
const createCarpoolCallback = useCallback(async () => { const createCarpoolCallback = useCallback(async () => {

View File

@ -12,7 +12,7 @@ export function useSignups() {
export function useMySignup() { export function useMySignup() {
const signups = useSignups(); const signups = useSignups();
const me = useMe()!; const me = useMe() || { id: 0, name: '' };
const signup = useMemo(() => signups[me.id] ?? null, [signups, me.id]); const signup = useMemo(() => signups[me.id] ?? null, [signups, me.id]);
@ -22,7 +22,7 @@ export function useMySignup() {
} }
export function useMyCarpool() { export function useMyCarpool() {
const me = useMe()!; const me = useMe() || { id: 0, name: '' };
const { event } = useContext(EventContext); const { event } = useContext(EventContext);
const carpool = useMemo( const carpool = useMemo(

View File

@ -1,22 +1,14 @@
import { useEffect, useState } from 'react';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import { getEvent } from '../api';
import Header from '../Header/Header'; import Header from '../Header/Header';
import { IEvent } from '../types';
import Event from './Event'; import Event from './Event';
export default function EventPage() { export default function EventPage() {
const id = +useParams<{ id: string }>().id; const id = +useParams<{ id: string }>().id;
const [event, setEvent] = useState<IEvent | null>(null);
useEffect(() => {
getEvent(id).then(setEvent);
}, [id]);
return ( return (
<> <>
<Header /> <Header />
{event ? <Event id={id} /> : <span>Loading...</span>} <Event id={id} />
</> </>
); );
} }

View File

@ -51,7 +51,9 @@ function GroupJoiner() {
{group && ( {group && (
<> <>
<br /> <br />
<span>Found group: {group.name}</span> <span>
Found group: <b>{group.name}</b>
</span>
<UIButton <UIButton
onClick={join} onClick={join}
style={!buttonEnabled ? { color: 'grey' } : {}} style={!buttonEnabled ? { color: 'grey' } : {}}

View File

@ -7,7 +7,7 @@ import GroupList from './GroupList';
export default function Groups() { export default function Groups() {
const [groups, setGroups] = useState<IGroup[]>([]); const [groups, setGroups] = useState<IGroup[]>([]);
// eslint-disable-next-line
useEffect(() => { useEffect(() => {
getGroups().then(setGroups); getGroups().then(setGroups);
}, []); }, []);

View File

@ -10,7 +10,7 @@ export default function UseImmutableTest() {
return ( return (
<div> <div>
{JSON.stringify(imm)} {JSON.stringify(imm)}
Reset button <br />
<button onClick={() => imm.z.a++}>Increment</button> <button onClick={() => imm.z.a++}>Increment</button>
<button onClick={() => imm.z.c.push(imm.z.c.length)}>Push</button> <button onClick={() => imm.z.c.push(imm.z.c.length)}>Push</button>
</div> </div>

View File

@ -1,74 +0,0 @@
import { createContext, ReactNode, useCallback, useState } from 'react';
import * as immutable from 'immutable';
import { useMemo } from 'react';
export class EventSignup extends immutable.Record({
userId: 0,
placeId: '',
formattedAddress: '',
latitute: 0,
longitude: 0,
}) {}
export class EventState extends immutable.Record({
id: 0,
name: '',
signups: immutable.Map<string, EventSignup>(),
}) {
addSignup(
userId: number,
placeId: string,
formattedAddress: string,
latitute: number,
longitude: number
) {
return this.set(
'signups',
this.signups.set(
userId.toString(),
new EventSignup({
userId,
placeId,
formattedAddress,
latitute,
longitude,
})
)
);
}
setName(name: string) {
return this.set('name', name);
}
}
type EventsProps = {
events: immutable.Map<number, EventState>;
upsertEvent: (event: EventState) => void;
};
export const EventsContext = createContext<EventsProps>({
events: immutable.Map<number, EventState>(),
upsertEvent: () => {},
});
export default function EventsProvider({ children }: { children: ReactNode }) {
const [events, setEvents] = useState(immutable.Map<number, EventState>());
const upsertEvent = useCallback(
(event: EventState) => {
setEvents(events.set(event.id, event));
},
[events]
);
const value: EventsProps = useMemo(() => {
return {
events,
upsertEvent,
};
}, [events, upsertEvent]);
return (
<EventsContext.Provider value={value}>{children}</EventsContext.Provider>
);
}

View File

@ -1,7 +0,0 @@
import { useContext } from 'react';
import { GroupsContext, GroupState } from './GroupsProvider';
export function useGroup(id: number): GroupState | null {
const { groups } = useContext(GroupsContext);
return groups.get(id, null);
}

View File

@ -1,105 +0,0 @@
import * as immutable from 'immutable';
import { ReactNode, useCallback, useMemo, useState } from 'react';
import { createContext } from 'react';
import { getGroup as fetchGroup } from '../components/api';
export class GroupState extends immutable.Record({
id: 0,
name: '',
memberIds: immutable.Set<number>(),
joinCode: null as string | null,
}) {
setJoinCode(joinCode: string) {
return this.set('joinCode', joinCode);
}
setName(name: string) {
return this.set('name', name);
}
addMember(memberId: number) {
return this.set('memberIds', this.memberIds.add(memberId));
}
removeMember(memberId: number) {
return this.set('memberIds', this.memberIds.remove(memberId));
}
setMemberIds(memberIds: immutable.Set<number>) {
return this.set('memberIds', memberIds);
}
}
function u(name: string) {
return () => {
throw new Error(`${name} is not implemented`);
};
}
type GroupContextProps = {
groups: immutable.Map<number, GroupState>;
addGroup: (group: GroupState) => void;
getGroup: (id: number) => Promise<GroupState | null>;
renameGroup: (groupId: number, name: string) => void;
generateJoinCode: (groupId: number) => void;
};
// A React context that provides access to the current user's groups.
export const GroupsContext = createContext<GroupContextProps>({
groups: immutable.Map<number, GroupState>(),
addGroup: u('addGroup'),
getGroup: u('getGroup'),
renameGroup: u('renameGroup'),
generateJoinCode: u('generateJoinCode'),
});
export default function GroupsProvider({ children }: { children: ReactNode }) {
// usestates for all the properties in GroupsContext
const [groups, setGroups] = useState(immutable.Map<number, GroupState>());
const addGroup = useCallback((group: GroupState) => {
setGroups((groups) => groups.set(group.id, group));
}, []);
const renameGroup = useCallback((groupId: number, name: string) => {
setGroups((groups) => groups.setIn([groupId, 'name'], name));
}, []);
const generateJoinCode = useCallback((groupId: number) => {
// TODO actually use the API here, this was generated by copilot
setGroups((groups) =>
groups.setIn(
[groupId, 'joinCode'],
Math.random().toString(36).substr(2, 5)
)
);
}, []);
const getGroup = useCallback(
async (id: number) => {
const group = groups.get(id);
if (group) {
return group;
} else {
const group = await fetchGroup(id);
const state = new GroupState({
id: group.id,
name: group.name,
});
addGroup(state);
return state;
}
},
[addGroup, groups]
);
const value: GroupContextProps = useMemo(() => {
return {
groups,
addGroup,
getGroup,
renameGroup,
generateJoinCode,
};
}, [addGroup, generateJoinCode, getGroup, groups, renameGroup]);
return (
<GroupsContext.Provider value={value}>{children}</GroupsContext.Provider>
);
}

View File

@ -13,16 +13,17 @@ export function useCancelCarpoolRequest() {
export function useInvitationState( export function useInvitationState(
carpoolId: number carpoolId: number
): 'invited' | 'requested' | 'none' { ): 'invited' | 'requested' | 'none' {
const notifications = useContext(NotificationsContext); const { invitedCarpoolIds, requestedCarpoolIds } =
useContext(NotificationsContext);
const invited = useMemo( const invited = useMemo(
() => notifications.invitedCarpoolIds.has(carpoolId), () => carpoolId in invitedCarpoolIds,
[carpoolId, notifications.invitedCarpoolIds] [carpoolId, invitedCarpoolIds]
); );
const requested = useMemo( const requested = useMemo(
() => notifications.requestedCarpoolIds.has(carpoolId), () => carpoolId in requestedCarpoolIds,
[carpoolId, notifications.requestedCarpoolIds] [carpoolId, requestedCarpoolIds]
); );
return invited ? 'invited' : requested ? 'requested' : 'none'; return invited ? 'invited' : requested ? 'requested' : 'none';

View File

@ -1,11 +1,11 @@
import { createContext, ReactNode, useCallback, useState } from 'react'; import { createContext, ReactNode, useCallback } from 'react';
import * as immutable from 'immutable';
import * as api from '../../components/api'; import * as api from '../../components/api';
import { useEffect } from 'react'; import { useEffect } from 'react';
import useImmutable from '../../components/useImmutable';
export const NotificationsContext = createContext({ export const NotificationsContext = createContext({
invitedCarpoolIds: immutable.Set<number>(), invitedCarpoolIds: {} as Record<number, boolean>,
requestedCarpoolIds: immutable.Set<number>(), requestedCarpoolIds: {} as Record<number, boolean>,
sendCarpoolRequest: (carpoolId: number) => sendCarpoolRequest: (carpoolId: number) =>
console.error('not implemented: sendCarpoolRequest'), console.error('not implemented: sendCarpoolRequest'),
@ -19,44 +19,47 @@ export default function NotificationsProvider({
}: { }: {
children: ReactNode; children: ReactNode;
}) { }) {
const [invitedCarpoolIds, setInvitedCarpoolIds] = useState( const [invitedCarpoolIds, setInvitedCarpoolIds] = useImmutable<
immutable.Set<number>() Record<number, boolean>
); >({});
const [requestedCarpoolIds, setRequestedCarpoolIds] = useImmutable<
const [requestedCarpoolIds, setRequestedCarpoolIds] = useState( Record<number, boolean>
immutable.Set<number>() >({});
);
useEffect(() => { useEffect(() => {
api.getSentRequestsAndInvites().then((invitations) => { api.getSentRequestsAndInvites().then((invitations) => {
setInvitedCarpoolIds((ids) => const invited = {} as Record<number, boolean>;
ids.concat( const requested = {} as Record<number, boolean>;
invitations for (let invitation of invitations) {
.filter((invite) => !invite.isRequest) if (invitation.isRequest) {
.map((invite) => invite.carpool.id) invited[invitation.carpool.id] = true;
) } else {
); requested[invitation.carpool.id] = true;
setRequestedCarpoolIds((ids) => }
ids.concat( }
invitations
.filter((invite) => invite.isRequest) setInvitedCarpoolIds(invited);
.map((invite) => invite.carpool.id) setRequestedCarpoolIds(requested);
)
);
}); });
}, []); }, [setInvitedCarpoolIds, setRequestedCarpoolIds]);
const sendCarpoolRequest = useCallback((carpoolId: number) => { const sendCarpoolRequest = useCallback(
api (carpoolId: number) => {
.sendCarpoolRequest(carpoolId) api.sendCarpoolRequest(carpoolId).then(() => {
.then(() => setRequestedCarpoolIds((ids) => ids.add(carpoolId))); requestedCarpoolIds[carpoolId] = true;
}, []); });
},
[requestedCarpoolIds]
);
const cancelCarpoolRequest = useCallback((carpoolId: number) => { const cancelCarpoolRequest = useCallback(
api (carpoolId: number) => {
.cancelCarpoolRequest(carpoolId) api.cancelCarpoolRequest(carpoolId).then(() => {
.then(() => setRequestedCarpoolIds((ids) => ids.delete(carpoolId))); delete requestedCarpoolIds[carpoolId];
}, []); });
},
[requestedCarpoolIds]
);
return ( return (
<NotificationsContext.Provider <NotificationsContext.Provider