Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Re-render on element removal #464

Open
RaoulBivolaru opened this issue Aug 25, 2023 · 5 comments
Open

Re-render on element removal #464

RaoulBivolaru opened this issue Aug 25, 2023 · 5 comments
Assignees
Labels
bug Something isn't working

Comments

@RaoulBivolaru
Copy link

Describe the bug
I'm using the 'vertical-stack' mode but I'm assuming this behaviour is for all modes.
I have a list of elements in the carousel and when I remove one element from the state, the carousel is getting re-render.
Is it possible somehow to remove an item and keep the current state of carousel as it is?

To Reproduce

RPReplay_Final1692970170.mov

Expected behaviour

Carousel should not re-render when removing an item from the data object.

Code

export default forwardRef(
  (props: MatchingCarouselProps, ref: React.Ref<ICarouselInstance>) => {
    const { data, setCurrentRcm } = props;

    return (
      <Carousel
        ref={ref}
        onSnapToItem={(idx) => setCurrentRcm(data[idx])}
        style={{
          width: "100%",
          height: CAROUSEL_HEIGHT,
          alignItems: "center",
          justifyContent: "center",
        }}
        loop={false}
        width={CAROUSEL_WIDTH}
        height={CAROUSEL_HEIGHT * 0.9}
        mode="vertical-stack"
        modeConfig={{
          moveSize: CAROUSEL_WIDTH * 1.3,
          showLength: data.length === 1 ? 1 : undefined,
        }}
        windowSize={100}
        data={data}
        renderItem={({ index, item }) => (
          <MatchItem
            key={item.userId}
            item={item}
            entering={FadeInDown.delay(index * 100).duration(200)}
          />
        )}
      />
    );
  },
);
<Actions
  item={currentRcm}
  onPress={() => {
    ref.current?.next();

    setTimeout(() => {
      setData((curr) =>
        curr.filter((rcm) => rcm.userId !== currentRcm?.userId),
      );
    }, 200);
  }}
/>

Versions (please complete the following information):

  • react: v18.2.0
  • react-native: v0.72.4
  • react-native-reanimated: v3.4.2
  • react-native-reanimated-carousel: v3.5.1
  • react-native-gesture-handler: v2.12.1

Smartphone (please complete the following information):

  • Device: [e.g. iPhone 13 Pro Max]
  • OS: [e.g. iOS16.6]
@RaoulBivolaru RaoulBivolaru added the bug Something isn't working label Aug 25, 2023
@appsgenie
Copy link

having the same issue. this happens anytime the data is changed.

@michaelkremenetsky
Copy link

having the same issue

@YaoHuiJi
Copy link

same here, data change of any single item will cause all items re-render, this behavior will cause serious performance issue.

Copy link

dosubot bot commented Jan 24, 2024

🤖

Hello @RaoulBivolaru! 👋

I'm Dosu, a friendly bot here to lend a hand with your issues and questions, and guide you through the path of becoming a contributor if you're keen! While we wait for a human maintainer, feel free to ask me anything. 😊

Based on the code you've provided, it seems like the carousel is re-rendering because the key for each item is changing when an item is removed. This is because the key for each item is its index in the data array. When an item is removed from the data array, the indices of all subsequent items change, causing them to re-render.

To prevent this, you can use a property of each item that is unique and stable as the key. For example, if each item has a unique userId property, you can use that as the key. Here's how you can modify the code:

return (
  <>
    {
      data.map((item, index) => {
        const realIndex = computedRealIndexWithAutoFillData({
          index,
          dataLength: rawDataLength,
          loop,
          autoFillData,
        });

        const { negativeRange, positiveRange } = displayedItems;
        const shouldRender = (index >= negativeRange[0] && index <= negativeRange[1])
          || (index >= positiveRange[0] && index <= positiveRange[1]);

        if (!shouldRender)
          return null;

        return (
          <BaseLayout
            key={item.userId} // Use item.userId as the key
            index={index}
            handlerOffset={offsetX}
            visibleRanges={visibleRanges}
            animationStyle={customAnimation || layoutConfig}
          >
            {({ animationValue }) =>
              renderItem({
                item,
                index: realIndex,
                animationValue,
              })
            }
          </BaseLayout>
        );
      })
    }
  </>
);

This assumes that each item in the data array has a unique userId property. If that's not the case, you'll need to find another property to use, or generate a unique id for each item when it's added to the array.

You can find more information about this in the ItemRenderer.tsx file in the repository.

Please let me know if this helps or if you have any other questions.

Sources


This response is meant to be useful and save you time. It is not meant to be a precise solution, but rather a starting point for your own research.

Help me be more useful! Please leave a 👍 if this is helpful and 👎 if it is irrelevant.

If you want to continue the conversation, start your reply with @dosu-bot.

@YaoHuiJi
Copy link

YaoHuiJi commented Jan 24, 2024

@dosu-bot 😄 it make sense, but maybe you should also mention @dohooo

Edit: I have tried to add a new prop 'keyExtractor' in patch-1, but it seems not work, all items still be re-rendered when any item changes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

5 participants