MyAccountContext.tsx 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. import React, { useReducer, createContext, useContext, useEffect } from 'react';
  2. import store from 'store';
  3. export const MY_ADDRESS = 'joy.myAddress';
  4. function readMyAddress (): string | undefined {
  5. const myAddress: string | undefined = store.get(MY_ADDRESS);
  6. console.log('Read my address from the local storage:', myAddress);
  7. return myAddress;
  8. }
  9. type MyAccountState = {
  10. inited: boolean;
  11. address?: string; // TODO rename to 'myAddress'
  12. };
  13. type MyAccountAction = {
  14. type: 'reload' | 'set' | 'forget' | 'forgetExact';
  15. address?: string;
  16. };
  17. function reducer (state: MyAccountState, action: MyAccountAction): MyAccountState {
  18. function forget () {
  19. console.log('Forget my address');
  20. store.remove(MY_ADDRESS);
  21. return { ...state, address: undefined };
  22. }
  23. let address: string | undefined;
  24. switch (action.type) {
  25. case 'reload': {
  26. address = readMyAddress();
  27. console.log('Reload my address:', address);
  28. return { ...state, address, inited: true };
  29. }
  30. case 'set': {
  31. address = action.address;
  32. if (address !== state.address) {
  33. if (address) {
  34. console.log('Set my new address:', address);
  35. store.set(MY_ADDRESS, address);
  36. return { ...state, address, inited: true };
  37. } else {
  38. return forget();
  39. }
  40. }
  41. return state;
  42. }
  43. case 'forget': {
  44. address = action.address;
  45. const isMyAddress = address && address === readMyAddress();
  46. if (!address || isMyAddress) {
  47. return forget();
  48. }
  49. return state;
  50. }
  51. default:
  52. throw new Error('No action type provided');
  53. }
  54. }
  55. function functionStub () {
  56. throw new Error('Function needs to be set in MyAccountProvider');
  57. }
  58. const initialState = {
  59. inited: false,
  60. address: undefined
  61. };
  62. export type MyAccountContextProps = {
  63. state: MyAccountState;
  64. dispatch: React.Dispatch<MyAccountAction>;
  65. set: (address: string) => void;
  66. forget: (address: string) => void;
  67. };
  68. const contextStub: MyAccountContextProps = {
  69. state: initialState,
  70. dispatch: functionStub,
  71. set: functionStub,
  72. forget: functionStub
  73. };
  74. export const MyAccountContext = createContext<MyAccountContextProps>(contextStub);
  75. export function MyAccountProvider (props: React.PropsWithChildren<{}>) {
  76. const [state, dispatch] = useReducer(reducer, initialState);
  77. useEffect(() => {
  78. if (!state.inited) {
  79. dispatch({ type: 'reload' });
  80. }
  81. }, [state.inited]); // Don't call this effect if `invited` is not changed
  82. const contextValue = {
  83. state,
  84. dispatch,
  85. set: (address: string) => dispatch({ type: 'set', address }),
  86. forget: (address: string) => dispatch({ type: 'forget', address })
  87. };
  88. return (
  89. <MyAccountContext.Provider value={contextValue}>
  90. {props.children}
  91. </MyAccountContext.Provider>
  92. );
  93. }
  94. export function useMyAccount () {
  95. return useContext(MyAccountContext);
  96. }