Last active
July 21, 2022 05:35
-
-
Save pjchender/2405030b8bfb8dda02394fc13fb098fb to your computer and use it in GitHub Desktop.
Recoil Pattern inspired from useContext
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { useCallback } from 'react'; | |
import { selector, atom, useRecoilState } from 'recoil'; | |
/** | |
* APIs | |
**/ | |
export enum STATE { | |
HAS_VALUE = 'hasValue', | |
HAS_ERROR = 'hasError', | |
LOADING = 'loading', | |
RESET = 'reset', | |
} | |
type User = { | |
id: number; | |
name: string; | |
username: string; | |
email: string; | |
}; | |
export const apiFetchUsers = async (): Promise<User[]> => { | |
console.log('[state/users] fetchUsers'); | |
const resp = await fetch('https://jsonplaceholder.typicode.com/users'); | |
await sleep(1000); | |
const data = await resp.json(); | |
return data; | |
}; | |
// STEP 1: define the default state | |
type UserStateType = { | |
state: `${STATE}`; | |
data: User[]; | |
}; | |
export const usersState = atom<UserStateType>({ | |
key: 'users', | |
default: { | |
state: STATE.LOADING, | |
data: [], | |
}, | |
}); | |
// STEP 2-1: create a custom hook | |
export const useUsers = () => { | |
const [users, setUsers] = useRecoilState(usersState); | |
// STEP 2-2 put async function or state manipulation here | |
const fetchUsers = useCallback(async () => { | |
setUsers({ | |
...users, | |
state: STATE.LOADING, | |
}); | |
const data = await apiFetchUsers(); | |
setUsers({ | |
state: STATE.HAS_VALUE, | |
data, | |
}); | |
}, [setUsers]); | |
const removeUser = useCallback( | |
(userId: number) => { | |
console.log('[useGetUsers] removeUser', userId); | |
setUsers({ | |
...users, | |
state: STATE.LOADING, | |
}); | |
const filteredData = users.data.filter((user) => user.id !== userId); | |
setUsers({ | |
data: filteredData, | |
state: STATE.HAS_VALUE, | |
}); | |
}, | |
[users] | |
); | |
const resetUser = useCallback(() => { | |
setUsers({ | |
state: STATE.RESET, | |
data: [], | |
}); | |
}, [setUsers]); | |
// STEP 2-3 return state and actions | |
return { users, actions: { fetchUsers, removeUser, resetUser } }; | |
}; | |
// STEP 3: define derived state if needed | |
type CurrentUserSelectorType = { | |
state: `${STATE}`; | |
data?: User; | |
}; | |
export const currentUserSelect = selector<CurrentUserSelectorType>({ | |
key: 'currentUser', | |
get: ({ get }) => { | |
const users = get(usersState); | |
const { state } = users; | |
switch (state) { | |
case STATE.HAS_VALUE: { | |
const [currentUser] = users.data; | |
return { | |
state, | |
data: currentUser, | |
}; | |
} | |
case STATE.RESET: | |
case STATE.LOADING: | |
case STATE.HAS_ERROR: { | |
return { | |
state, | |
}; | |
} | |
} | |
}, | |
}); | |
// other utilities | |
export function sleep(ms) { | |
return new Promise((resolve) => setTimeout(resolve, ms)); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { STATE, useUsers } from '@/state/user'; | |
import { useEffect } from 'react'; | |
const UserList = () => { | |
// STEP 4: useUser to get data from state and actions | |
const { users, actions } = useUsers(); | |
const { removeUser, fetchUsers, resetUser } = actions; | |
useEffect(() => { | |
// if you want to get the newest users every time, otherwise the cache will be used | |
fetchUsers(); | |
return () => { | |
// if you want to reset user when the component is unmounted | |
return resetUser(); | |
}; | |
}, []); | |
return ( | |
<div> | |
<button type="button" onClick={() => fetchUsers()}> | |
fetch users | |
</button> | |
<div>{users.state === STATE.LOADING && <p>loading...</p>}</div> | |
<ul> | |
{users.state === STATE.HAS_VALUE && | |
users.data.map((user) => ( | |
<li key={user.id}> | |
{user.name} | |
<button | |
type="button" | |
onClick={() => { | |
removeUser(user.id); | |
}} | |
> | |
X | |
</button> | |
</li> | |
))} | |
</ul> | |
</div> | |
); | |
}; | |
export default UserList; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment