I have a hook called useQueryEvents that 1) fetches all past transactions for a user and 2) listens to the network for incoming/outgoing transactions. In both cases the transactions are passed into a function addActionToActivity that simply appends it to the activity array and updates it in the context state under the key activity.
I can't get the activity to sync correctly. Whenever the state updates it does not have the last transaction because it's always one step behind. If I add activity to the dependancy it works but then starts a new listener (due to the whole function being called again with the new activity value) which causes an infinity-like-loop which keeps switching up the state.
function useQueryEvents() {
const { state: { connectedNetwork, selectedWallet, activity },
} = useContext(LocalContext);
useEffect(() => {
async function bootstrapQueryEvents() {
// First get all the past transactions
const transactions = await getAllPastTransactions();
const contract = await getContract();
// Now save them to context state via addActionToActivity
await addActionToActivity(transactions, activity);
// Now that all the past transactions have been saved
// listen to all incoming/outgoing transactions and
// save to context state via addActionToActivity
contract.on('Transfer', async (from, to, amount, event) => {
console.log(`${from} sent ${ethers.utils.formatEther(amount)} to ${to}`);
const transaction = await formatEventToTransaction(event);
await addActionToActivity(transaction, activity);
});
}
bootstrapQueryEvents();
}, [selectedAsset, connectedNetwork, selectedWallet]); // <- I've tried adding `activity` here
}
addActionToActivity (redacted unnecessary code) - all it does is do fetch relevant data and appends the newly concatenated transactions array onto activity then calls setAsyncValue (see below for that function)
export async function addActionToActivity(transaction, activity) {
return new Promise((resolve, reject) => {
async function handleAdd() {
const { username, selectedAsset } = getUserInfo();
const parsedActivity = JSON.parse(activity);
const { userActivity, assetActivity } = getSpecificActivity();
// Add the transaction(s) onto the existing activity
const updatedAssetSpecificActivity = assetActivity.concat(transaction);
const removedDuplicates = removeDuplicateTransactions(updatedAssetSpecificActivity, hashes);
const updatedActivity = {
...parsedActivity,
[username]: {
...userActivity,
[selectedAsset]: removedDuplicates,
},
};
try {
await setAsyncValue('activity', JSON.stringify(updatedActivity));
resolve();
} catch {
reject();
}
}
handleAdd();
});
}
Local Context Contents (redacted unnecessary code) - setAsyncValue simply stores the activity to local storage then updates the context state using a dispatch from a reducer in the context.
function localReducer(prevState, action) {
switch (action.type) {
case "UPDATE_VALUE":
return {
...prevState,
[action.payload.key]: action.payload.value,
};
default:
return prevState;
}
}
const [state, dispatch] = useReducer(localReducer, initialLocalState);
function updateValue(key, value, dispatch): {
dispatch({
type: 'UPDATE_VALUE',
payload: { key, value },
});
}
const localContext = useMemo(() => ({
state,
dispatch,
setAsyncValue: async (key: string, value: string) => {
await storeAsyncData(`@${key}`, value);
updateValue(key, value, dispatch);
},
}));
Any ideas how I can approach updating the state while having access to the updated activity value inside the listener without starting a new instance of the listener? Or maybe there's a different approach I can take altogether?
Thanks in advance
from Update state within listener that is inside useEffect
No comments:
Post a Comment