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

List Item not re-render when renderItem changes #699

Open
2 tasks done
mk-nickyang opened this issue Dec 5, 2022 · 11 comments
Open
2 tasks done

List Item not re-render when renderItem changes #699

mk-nickyang opened this issue Dec 5, 2022 · 11 comments
Labels
bug Something isn't working

Comments

@mk-nickyang
Copy link

mk-nickyang commented Dec 5, 2022

Current behavior

List Item does not re-render when renderItem prop changes.

Expected behavior

If this is a bug, List Item should re-render when renderItem prop changes,
If this is expected, I think it should be mentioned in the docs since it's different from the RN FlatList behaviour.

To Reproduce

https://snack.expo.dev/@lucissa/shopify-flashlist-renderitem
(renderItem gets updated when the time state updates, but List Item still shows the initial time)

Platform:

  • iOS
  • Android

Environment

1.4.0

@mk-nickyang mk-nickyang added the bug Something isn't working label Dec 5, 2022
@kubilaysalih
Copy link
Contributor

If the keyExtractor stays the same all the time, items wont be rerendered due to optimization. Technically your list items are updated every three seconds. We are sure that your elements are different from the previous items, so you need to consider this and change keyExtractor function.

For example: https://snack.expo.dev/@kubilaysalih/9cb1c2

@mk-nickyang
Copy link
Author

Yeah, I guess there are a couple of ways to trigger re-render manually, like using extraData (from #585 (comment)), or using keyExtractor like you said, or even just change the data prop.

I'm more just want to confirm if this is an expected behaviour due to the FlashList optimization logic. And as I said in the issue above, I think it would be a good idea to mention this in the docs.

@naqvitalha
Copy link
Collaborator

We'd really appreciate a PR updating the documentation :)

@hotaryuzaki
Copy link

hotaryuzaki commented Jan 16, 2023

we found a similar issue if we have a checkbox in the list, the checkbox does not re-render when we checked.
i think it will be heavy if we change all the keys in every checked checkbox.
so we use extraData and give them all the objects data that is checked.

@matheusmaske
Copy link

@hotaryuzaki your answer help-me a lot, thanks!

@andresfmm
Copy link

andresfmm commented Jun 18, 2023

@hotaryuzaki thanks for your answer i leave a example here base in your answer with checkbox

const [data, setData] = useState([{"checked": true, "code_postal": "760013", "id": "22943e1e-f6be-11ed-8207-9828a60f7a9e", "name": "one"}, {"checked": false, "code_postal": "760042", "id": "229444b3-f6be-11ed-8207-9828a60f7a9e", "name": "two"}])

const [extradata, setExtraData] = useState(null);

const keyExtractor = (item:any) => item.id;

const onItemSelected = (item:any) => {

item.checked = !item.checked;

    for (let index in data) {
        if (data[index].id === item.id) {
            extradata == item.id ? setExtraData(null) : setExtraData(item.id)
        }
    }
}

const elementRender = ({item}:any) => {

    return (
    <TouchableOpacity
        onPress={() => onItemSelected(item)} 
        activeOpacity={0.5}>
        <Checkbox.Item label={item.name} status={ item.checked ? 'checked' : 'unchecked' } />
    
    </TouchableOpacity>
    )
}

<FlashList
    data={data}
    extraData={extradata}
    keyExtractor={keyExtractor}
    renderItem={ elementRender }
    estimatedItemSize={700}
/>

@Hosam-hsm
Copy link

Hosam-hsm commented Mar 26, 2024

I have a multi select list in which items can be marked as selected. I'm using a state to pass the data prop to the Flashlist and I'm storing the selected item in a separate state. An already selected item will get deselected if pressed again.

An item gets marked as selected and deselected on first and second press as expected. My issue is that the list is failing to re-render after that in android even if I am passing the extraData prop. A previously deselected item is not getting marked as selected upon new press. It is working as expected in iOS though with the extraData prop. Am I missing something here? Any help will be appreciated.

const [assets, setAssets] = useState<AssetType[]>([
    {
      id: "1",
      uri: "file:///storage/emulated/0/Pictures/image-7 (1).jpg",
    },
  ]);

  const [selectedMedia, setSelectedMedia] = useState<AssetType[]>([]);

  const onPressMedia = useCallback((media: AssetType) => {
    setSelectedMedia((selected) => {
      const index = selected.findIndex((m) => m.id === media.id);
      if (index !== -1) {
        return [...selected.slice(0, index), ...selected.slice(index + 1)];
      } else {
        return [...selected, media];
      }
    });
  }, []);


  const renderItem = useCallback(
    ({ item, index }) => {
      return (
        <List
          item={item}
          onPressMedia={onPressMedia}
          selectedMedia={selectedMedia}
        />
      );
    },
    [selectedMedia],
  );

and inside return I'm using 

    <FlashList
          estimatedItemSize={height / 6}
          data={assets}
          numColumns={3}
          keyExtractor={(item) => item.id}
          renderItem={renderItem}
          extraData={selectedMedia}
        />


and inside the List component

 const indexInSelectedMedia = selectedMedia.findIndex((m) => m.id === item.id);
return (
 {indexInSelectedMedia > -1 && (
        <View
          position="absolute"
          height={24}
          width={24}
          style={{
            backgroundColor: "#1D3557",
          }}
        >
          <Text variant="body-s-bold" color="textOnPrimary">
            {indexInSelectedMedia + 1}
          </Text>
        </View>
      )}

I tried adding the extraData prop as mentioned in the documents which will trigger a re-render. But it only re-renders twice in android. After that a previously deselected item is not getting marked as selected on press. I'm using the latest version of Flashlist(1.6.3) and expo(50.0.14).

@audn
Copy link

audn commented Aug 4, 2024

Can you give this a try?
I got it working by extracting extraData from renderItem().

const renderItem = useCallback(
  ({ item, index, extraData }) => {
    return (
      <List item={item} onPressMedia={onPressMedia} selectedMedia={extraData} />
    );
  },
  [selectedMedia]
);

<FlashList
  estimatedItemSize={height / 6}
  data={assets}
  numColumns={3}
  keyExtractor={(item) => item.id}
  renderItem={renderItem}
  extraData={selectedMedia}
/>;

cc @Hosam-hsm

@frozencap
Copy link

extraData

it works, but why is this even necessary

@Ajmal0197
Copy link

This worked for me:

  const [listData, setListData] = useState([]);

  const setLongDescItemFun = (v) => {
    setLongDescItem(v);
    setListData([...data]);
  };


      <FlashList
        data={data}
        extraData={listData}
        ...
       />

@enzzoperez
Copy link

If you just need to change or test your list ui, you can add extraData={[]} to the list and with this could use the hot reload. Then you must delete it

<FlashList
        data={data}
        extraData={[]}
        ...
 />

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