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

Active Item (UseState) #764

Closed
ValyaBabenkov opened this issue Feb 10, 2023 · 6 comments
Closed

Active Item (UseState) #764

ValyaBabenkov opened this issue Feb 10, 2023 · 6 comments
Labels
bug Something isn't working

Comments

@ValyaBabenkov
Copy link

Good afternoon.
I use FlashList, but it does not work to display the active item in the list.
Here is the code:

<FlashList
            // bounces={false}
            // estimatedItemSize={240}
            showsHorizontalScrollIndicator={false}
            initialScrollIndex={currentIndex}
            data={items.value}
            renderItem={({item, index}) => (
              <Item
                {...{
                  ...item,
                  isActive: item.id === activeItem.id,
                  onPress: () => setActiveItem(items.value[index]),
                }}
              />
            )}
          />


const Item = ({title, onPress, isActive}: ItemProps) => {
  const listActive = useRef<boolean>(isActive);
  const [active, setActive] = useState<boolean>(isActive);
  const tw = useTailwind();

  if (active !== listActive.current) {
    listActive.current = active;
    setActive(active);
  }

  return (
    <TouchableOpacity onPress={onPress}>
      <Text style={tw(`py-10 mb-4 ${active ? 'bg-blue-300' : 'bg-red-100'}`)}>
        {title}
      </Text>
    </TouchableOpacity>
  );
};

Can you advise who faced with this?
I'm following the instructions (https://shopify.github.io/flash-list/docs/fundamentals/performant-components)
Thanks

@ValyaBabenkov ValyaBabenkov added the bug Something isn't working label Feb 10, 2023
@hirbod
Copy link
Contributor

hirbod commented Feb 12, 2023

Your code is wrong. You should follow this advice:
https://shopify.github.io/flash-list/docs/recycling

const Item = ({title, onPress, isActive, ...props}: ItemProps) => {
  const lastItemId = useRef<number>(props.id)
  const [active, setActive] = useState<boolean>(isActive);
  const tw = useTailwind();

  if (lastItemId.current !== props.id) {
    lastItemId.current = props.id
    setActive(isActive);
  }

  return (
    <TouchableOpacity onPress={onPress}>
      <Text style={tw(`py-10 mb-4 ${active ? 'bg-blue-300' : 'bg-red-100'}`)}>
        {title}
      </Text>
    </TouchableOpacity>
  );
};

Untested, but it should work. FlashList recycles components, and as a result, it recycles state. To detect if the current item has changed (using the ID as an example), you should store the element ID inside a ref. When you detect that the current ID does not match the last item ID, you should reset every state or set the actual value you need.

However, based on your example, you don't need to use useState, as you are already passing isActive as a prop. You could access it directly, and it would work. But since you are likely going to change the state within onPress, my proposed solution would still be valid. If possible, try to avoid using useState as it may decrease performance slightly, but the impact is negligible.

@muammadibal
Copy link

muammadibal commented Mar 10, 2023

i'm used it as the documentation and you tell but still not work. data in renderitem component not correct

LeeWannacott added a commit to LeeWannacott/public-audiobooks that referenced this issue Mar 21, 2023
This reverts commit 9fe4169.

We are temporarily reverting changing to flashlist because it breaks my button for shelving (it doesn't render when clicking on it, you have to scroll down and back up for it to render) and if I have an accordion open and scroll down it opens them on the other audiobooks

Try and implement this feature on a branch in the future. Might need to
useRef() or something... Shopify/flash-list#764
@fortmarek
Copy link
Contributor

This doesn't seem to be a bug, for questions you can refer to Github discussions. The @hirbod's suggestion seems to be correct 👍

@AhmedAbuelenin
Copy link

@hirbod
I am having a simple list with item component consists of a text and checkbox, when checkbox is changed to checked state and start to scroll down I find slow update in other checkboxs state from checked to unchecked and this doesn't look good regarding to UX.

This is my code similar to yours and to the docs

const AppCheckbox = ({item, onCheck}: AppCheckboxProps) => {
  const lastItemId = useRef(item.id);
  const [isChecked, setIsChecked] = useState(false);

  const updateItemId = () => {
    lastItemId.current = item.id;
  };

  if (item.id !== lastItemId.current) {
    updateItemId();
    setIsChecked(Boolean(item.isChecked));
  }

  const handleChecking = () => {
    onCheck({id: item.id, isChecked: !isChecked});
    updateItemId();
    setIsChecked(value => !value);
  };

  return <CheckBox value={isChecked} onValueChange={handleChecking} />;
};

I am using react native 0.72.3 and latest stable version of flash-list.

Thank you.

@hirbod
Copy link
Contributor

hirbod commented Dec 7, 2023

You might need to increase drawDistance to a bigger value (default is 250).

@jvgeee
Copy link

jvgeee commented Sep 25, 2024

If anyone else comes across this issue while I did, where you're trying to pipe in data to the renderItem component but it's not working - you need to use the (not-well-documented) extraData prop

E.g.:

const [currentIndex, setCurrentIndex] = useState(0)

  function handleScroll(event){

  const x = e.nativeEvent.contentOffset.x

      // find the index of the current userRallies item which is in view, knowing that the width of each item is CAROUSEL_ITEM_WIDTH
      const index = Math.round(x / (CAROUSEL_ITEM_WIDTH + 15))

      setCurrentIndex(index || 0)
  }

  return <FlashList
            data={currentRallyBucket}
            estimatedItemSize={CAROUSEL_ITEM_WIDTH + 20}
            renderItem={({ item, index, extraData }) => (
              <ItemRender
                item={item}
                isCurrent={extraData.currentIndex === index}
                itemWidth={extraData.CAROUSEL_ITEM_WIDTH}
              />
            )}
            extraData={{ currentIndex, CAROUSEL_ITEM_WIDTH }}
          /> 

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

6 participants