简体   繁体   中英

context api - useEffect doesn't fire on first render - react native

The useEffect doesn't fire on first render, but when I save the file (ctrl+s), the state updates and the results can be seen.

What I want to do is, when I'm in GameScreen , I tap on an ICON which takes me to WalletScreen , from there I can select some items/gifts ( attachedGifts - in context) and after finalising I go back to previous screen ie GameScreen with gifts attached ( attachedGifts!==null ), now again when I tap ICON and go to WalletScreen it should show me the gifts that were attached so that I could un-attach them or update selection (this is being done in the useEffect below in WalletScreen ), but the issue is, although my attachedGifts state is updating, the useEffect in WalletScreen does not fire immediately when navigated, when I hit ctrl+s to save the file, then I can see my selected/attached gifts in WalletScreen .

code:

const Main = () => {
  return (
  <GiftsProvider>
    <Stack.Screen name='WalletScreen' component={WalletScreen} />
    <Stack.Screen name='GameScreen' component={GameScreen} />
  </GiftsProvider>
  )
};

const GameScreen = () => {
  const { attachedGifts } = useGifts(); //coming from context - GiftsProvider
  console.log('attached gifts: ', attachedGifts);

  return ...
};

const WalletScreen = () => {
  const { attachedGifts } = useGifts();

  useEffect(() => { // does not fire on initial render, after saving the file, then it works.
        if (attachedGifts !== null) {
            let selectedIndex = -1
            let filteredArray = data.map(val => {
                if (val.id === attachedGifts.id) {
                    selectedIndex = walletData.indexOf(val);
                    setSelectedGiftIndex(selectedIndex);
                    return {
                        ...val,
                        isSelect: val?.isSelect ? !val?.isSelect : true,
                    };
                } else {
                    return { ...val, isSelect: false };
                }
            });
            setData(filteredArray);
        }
    }, [attachedGifts]);

  const attachGiftsToContext = (obj) => {
    dispatch(SET_GIFTS(obj));
    showToast('Gifts attached successfully!');
    navigation?.goBack(); // goes back to GameScreen
  }

  return (
    // somewhere in between
    <TouchableOpacity onPress={attachGiftsToContext}>ATTACH</TouchableOpacity>
  )
};

context:

import React, { createContext, useContext, useMemo, useReducer } from 'react';

const GiftsReducer = (state: Object | null, action) => {
    switch (action.type) {
        case 'SET_GIFTS':
            return action.payload;
        default:
            return state;
    }
};

const GiftContext = createContext({});

export const GiftsProvider = ({ children }) => {
    const initialGiftState: Object | null = null;

    const [attachedGifts, dispatch] = useReducer(
        GiftsReducer,
        initialGiftState,
    );

    const memoedValue = useMemo(
        () => ({
            attachedGifts,
            dispatch,
        }),
        [attachedGifts],
    );

    return (
        <GiftContext.Provider value={memoedValue}>
            {children}
        </GiftContext.Provider>
    );
};

export default function () {
    return useContext(GiftContext);
}

Output of console.log in GameScreen :

attached gifts:  Object {
  "reciptId": "baNlCz6KFVABxYNHAHasd213Fu1",
  "walletId": "KQCqSqC3cowZ987663QJboZ",
}

What could possibly be the reason behind this and how do I solve this?

EDIT

Added related code here: https://snack.expo.dev/uKfDPpNDr

From the docs

When you call useEffect in your component, this is effectively queuing or scheduling an effect to maybe run, after the render is done.

After rendering finishes, useEffect will check the list of dependency values against the values from the last render, and will call your effect function if any one of them has changed.

You might want to take a different approach to this. There is not much info, but I can try to suggest to put it into render, so it might look like this

const filterAttachedGifts = useMemo(() => ...your function from useEffect... , [attachedGitfs])

Some where in render you use "data" variable to render attached gifts, instead, put filterAttachedGifts function there.

Or run this function in component body and then render the result.

const filteredAttachedGifts = filterAttachedGifts()

It would run on first render and also would change on each attachedGifts change.

If this approach doesn't seems like something that you expected, please, provide more code and details

UPDATED

I assume that the problem is that your wallet receive attachedGifts on first render, and after it, useEffect check if that value was changed, and it doesn't, so it wouldn't run a function.

You can try to move your function from useEffect into external function and use that function in 2 places, in useEffect and in wallet state as a default value

feel free to pick up a better name instead of "getUpdatedArray"

const getUpdatedArray = () => {
    const updatedArray = [...walletData];
    if (attachedGifts !== null) {
        let selectedIndex = -1
        updatedArray = updatedArray.map((val: IWalletListDT) => {
            if (val?.walletId === attachedGifts?.walletIds) {
                selectedIndex = walletData.indexOf(val);
                setSelectedGiftIndex(selectedIndex);
                setPurchaseDetailDialog(val);
                return {
                    ...val,
                    isSelect: val?.isSelect ? !val?.isSelect : true,
                };
            } else {
                return { ...val, isSelect: false };
            }
        });
    }    
    return updatedArray;
}

Then use it here

const [walletData, setWalletData] = useState(getUpdatedArray());

and in your useEffect

useEffect(() => {
    setWalletData(getUpdatedArray());
}, [attachedGifts]);

That update should cover the data on first render. That might be not the best solution, but it might help you. Better solution require more code\time etc.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM