0

Playing with Fluent UI DetailsList, I've created this solution. I've deleted all unnecessary lines to minimize the problem, as it appears that when logging in onColumnClick then somehow it doesn't print the current state but the initial state, why is this happening:

import * as React from 'react';
export const ClvKkcgListyHistorie = (props: IClvKkcgListyHistorieWebPartProps) => {
const [notifications, setNotifications] = React.useState<INotificationDetailsList[]>([]);
const [columns, setColumns] = React.useState<IColumn[]>();

React.useEffect(() => {
  setColumns(createColumns(onTitleClick, onColumnClick));
}, []);

// With this useEffect everything works fine
React.useEffect(() => {
  setColumns(createColumns(onTitleClick, onColumnClick));
}, [notifications]);

React.useEffect(() => {
  const fetchAllNotifications = async () => {
   // Fetch items here
  }
  fetchAllNotifications();
}, [])


const onColumnClick = (e: any, column: IColumn) => {
  console.log(notifications,columns, "1" ); 
  // Here I get [], undefined, "1"
}

return (
  <>
  {notifications.length !== 0 && 
    <DetailsList 
      items={notifications} 
      columns={columns}
    >
    </DetailsList>
  }
  </>
);
}

Columns.tsx

import * as React from 'react';


export const createColumns = (onTitleClick: any, onColumnClick: any) : IColumn[] => {
  return [
    {
      key: "title", 
      name: "Nadpis", 
      minWidth: 70,
      maxWidth: 100,
      isResizable: true,
      isRowHeader: true,
      isSorted: false,
      isSortedDescending: false,
      sortAscendingAriaLabel: 'A - Z',
      sortDescendingAriaLabel: 'Z - A',
      isPadded: true,
      onColumnClick: onColumnClick,
      onRender: (item: INotificationDetailsList) => {
        return <span>{item.Title}</span>;
      }
    },

    }
  ]
} 

In that

1 Answers1

1

When you are rendering your component, it creates this function onColumnClick and looks at what the columns are in this very moment. It passes that function into the handlers, remembering what those columns were. (In your case, an empty array.)

In order to update the function when columns update, you need to use useCallback. It is a similar pattern to useEffect, but returns a function instead of runs a function whenever the dependencies change.

const onColumnClick = React.useCallback((e: any, column: IColumn) => {
  console.log(notifications,columns, "1" ); 
  // This will return a different function to the handlers
  // whenever columns changes, but not notifications
}, [columns])
HaveSpacesuit
  • 3,572
  • 6
  • 40
  • 59
  • How can I tell that it creates the function which "remembers" a state at the time of passing? How is it different from a normal callback? I have just never encountered a function like this, "classic" callbacks work fine every time :) EDIT: I mean how do I know that I will need React.useCallback instead of normal callback? – Luci Castle Dec 07 '20 at 14:15
  • The best advice I can give is encourage you to carve out some time to read this article. https://overreacted.io/a-complete-guide-to-useeffect/ It is quite informative on how state works differently than you might think in React. Understanding the concepts in the article will help you recognize this type of error quicker in the future. – HaveSpacesuit Dec 07 '20 at 19:49