0

I am learning react, and given this simple example of using SWR to fetch some items from an API and showing the items with groups using fluentui DetailedList - I am running into a problem with the groups.

Whenever I click a group in UI to collapse/uncollapse, that seems to trigger a rerender, and then the component will createGroups(data) again which resets the UI again back to original state as the groups object is recalculated.

Where am I supposed to actually store / calculate the groups information of my data? Initial it needs to be created, but from there it seems that it should only needs to be reevaluated whenvere the swr api returns new data - and then i still properly would want to merge in the current state from collapsed groups that the user might have changed in the UI.

Is it because i properly should not use SWR as it refreshes data live - and only do it on page refresh?

const SWR = ({ children, listid, onSuccess }: { children: ((args: SWRResponse<any, any>) => any), listid: string, onSuccess?: any }) => {
  const url = `http://localhost:7071/api/Lists/${listid}`;
  console.log(url);
  const {data,error } = useSWR(url, { fetcher: fetcher, isPaused: () => listid === undefined, onSuccess });

  const items = data.value;
  const groups = createGroups(data)

  return <... DetailsList group={groups} items={items} ... >; // ... left out a few details ...
};
Poul K. Sørensen
  • 16,950
  • 21
  • 126
  • 283
  • Might also be the underlaying problem that whenever user click collapse on group , swr refreshes data as part of rerender - is this what one want? – Poul K. Sørensen Mar 18 '21 at 21:52

1 Answers1

0

What about adding a state for holding the groups and an useEffect for when data changes and insde the useEffect you should check if the content has changed before updating the groupState.

const hasChanged(data) => {
    return data.notEquals(state.data)); // write your own logic for comparing the result
};
useEffect(() => { if (hasChanged(data)) { 
    setState(prev=> ({ ...prev, group: createGroup(data), data: data });
}}, [data]);

You dont actually need to store the group, you can just hold the data in your state, but the important part is to be able to check if any change actually took place before changing the state.

Another thing worth trying is the compare option in the useSWR hook. So instead of placing the "hasChanged" logic inside an useEffect hook, perhaps it could be in the compare function. Haven't had the chanse to test this myself though.

A third and final option would be to place the creation of groups inside your fetcher. Perhaps the most intuitive solution for this particular case, though I'm not completely sure it will prevent the unnecessary re-renders.

const fetcher = url => axios.get(url).then(res=> {
    return {
      items: res.data.value,
      groups: createGroups(res.data),
    };
});

const SWR = ({ children, listid, onSuccess }: { children: ((args: SWRResponse<any, any>) => any), listid: string, onSuccess?: any }) => {
  const { data, error } = useSWR(url, fetcher, ...);
  return <... DetailsList group={data.groups} items={data.items} ... >; // ... left out a few details ...
};
Tiago Redaelli
  • 560
  • 5
  • 17