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 { BrowserRouter, Route, Switch } from 'react-router-dom';
import GroupsProvider from '../state/GroupsProvider';
import NotificationsProvider from '../state/Notifications/NotificationsProvider';
import { useMe } from './hooks';
import UseImmutableTest from './UseImmutableTest';
import WheelShare from './WheelShare';
import WheelShareLoggedOut from './WheelShareLoggedOut';
@ -26,8 +26,8 @@ export default function App() {
return (
<NotificationsProvider>
<GroupsProvider>
<div style={{ padding: '1rem', maxWidth: '100vw' }}>
<UseImmutableTest />
<div style={style}>
<BrowserRouter>
<Switch>
@ -49,7 +49,6 @@ export default function App() {
</BrowserRouter>
</div>
</div>
</GroupsProvider>
</NotificationsProvider>
);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -10,7 +10,7 @@ export default function UseImmutableTest() {
return (
<div>
{JSON.stringify(imm)}
Reset button
<br />
<button onClick={() => imm.z.a++}>Increment</button>
<button onClick={() => imm.z.c.push(imm.z.c.length)}>Push</button>
</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(
carpoolId: number
): 'invited' | 'requested' | 'none' {
const notifications = useContext(NotificationsContext);
const { invitedCarpoolIds, requestedCarpoolIds } =
useContext(NotificationsContext);
const invited = useMemo(
() => notifications.invitedCarpoolIds.has(carpoolId),
[carpoolId, notifications.invitedCarpoolIds]
() => carpoolId in invitedCarpoolIds,
[carpoolId, invitedCarpoolIds]
);
const requested = useMemo(
() => notifications.requestedCarpoolIds.has(carpoolId),
[carpoolId, notifications.requestedCarpoolIds]
() => carpoolId in requestedCarpoolIds,
[carpoolId, requestedCarpoolIds]
);
return invited ? 'invited' : requested ? 'requested' : 'none';

View File

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