4

I am trying to initialize the state during the mounting cycle, and then do something with it on each update.

However, the state gets reset somehow? I don't understand why.

const [journalItems, setJournalItems] = useState([]);

useEffect(() => {
    fetch(`http://localhost:4000/journals/${props.match.params.key}/items`)
        .then(res => res.json())
        .then(data => {
            setJournalItems(data)    // Sets the state when the AJAX completes
            })
        .catch(err => err);

    table.current = new Tabulator(refTable.current, {
        rowClick: function (e, row) {
            console.log("tabulator journalitems", journalItems) //  State is lost returns []
            handleTableRowClick(row._row.data.id)
        },
        columns: [
            { title: "Компанија", field: "companyName" },
            { title: "Документ", field: "documentKey" },
        ],
    });
}, []);

useEffect(() => {
    console.log("useEffect journalItems", journalItems)    // Works fine
    table.current.replaceData(journalItems)                // Uses the new state
}, [journalItems]);

function handleTableRowClick(journalItemId) {
    console.log("handletablerowclick journalitems", journalItems) // State is lost, resets to []
}

Results form the console logs...

useEffect journalItems []
useEffect journalItems (7) [{…}, {…}, {…}, {…}, {…}, {…}, {…}]

tabulator journalitems []
handleTableRowClick journalitems []
Ivan
  • 1,967
  • 4
  • 34
  • 60
  • How many times do you see your console log? A `useEffect` with dependencies will actually still fire when the component mounts just like it does with `[]`. So I would expect to see one log with an empty array, and then a second if your update is successful. – Brian Thompson Feb 22 '20 at 04:14
  • Yes, I do see it twice. 1. `useEffect journalItems []`, 2. `useEffect journalItems (7) [{…}, {…}, {…}, {…}, {…}, {…}, {…}]`. However, the state in the click handler is empty... – Ivan Feb 22 '20 at 04:17
  • Ok, your update makes the question clearer. – Brian Thompson Feb 22 '20 at 04:19
  • I am reading for a [possible solution here](https://reactjs.org/docs/hooks-faq.html#what-can-i-do-if-my-effect-dependencies-change-too-often), but it makes no sense why this happens... – Ivan Feb 22 '20 at 04:22
  • @Ivan , may be this can help you https://stackoverflow.com/questions/54069253/usestate-set-method-not-reflecting-change-immediately . State change is asynchronous . – Harmandeep Singh Kalsi Feb 22 '20 at 04:52
  • @HarmandeepSinghKalsi Thanks for the suggestion. I managed to solve it, but it's very weird... Can you please check it and tell me if it's ok? – Ivan Feb 22 '20 at 05:01
  • @BrianThompson Can you please check my answer? It works that way, but I am not sure if it's correct. – Ivan Feb 22 '20 at 05:04

2 Answers2

1

I managed to solve it in a weird way with useRef for the function variable, and by moving the function definition inside the useEffect?

const [journalItems, setJournalItems] = useState([]);

let handleTableRowClick = useRef(null);

useEffect(() => {
    fetch(`http://localhost:4000/journals/${props.match.params.key}/items`)
        .then(res => res.json())
        .then(data => {
            setJournalItems(data)    // Sets the state when the AJAX completes
            })
        .catch(err => err);

    table.current = new Tabulator(refTable.current, {
        rowClick: function (e, row) {
            console.log("tabulator journalitems", journalItems) //  State is lost returns []
            handleTableRowClick.current(row._row.data.id)
        },
        columns: [
            { title: "Компанија", field: "companyName" },
            { title: "Документ", field: "documentKey" },
        ],
    });
}, []);

useEffect(() => {
    console.log("useEffect journalItems", journalItems)    // Works fine
    table.current.replaceData(journalItems)                // Uses the new state

    handleTableRowClick.current = (journalItemId) => {
        console.log("handletablerowclick journalItems", journalItems)
        // Find the clicked row from all the rows
        let journalItem = journalItems.filter(item => item.id === journalItemId)[0]
        setFormJournalItems(journalItem)
    }
}, [journalItems]);
Ivan
  • 1,967
  • 4
  • 34
  • 60
0

I believe this is happening because the initial value for journalItems is being closed over inside the function defined for the rowClick property.

Because the effect containing the table.current is only run once, that table.current value gets all initial values and never gets updated with new handlers for the rowClick prop.

I suspect that you might need to make the changes below to get things moving along:

const [journalItems, setJournalItems] = useState([]);

useEffect(() => {
    fetch(`http://localhost:4000/journals/${props.match.params.key}/items`)
        .then(res => res.json())
        .then(data => {
            setJournalItems(data)    // Sets the state when the AJAX completes
            })
        .catch(err => err);
}, [props.match.params.key]);

// Re-create a new `table.current` value (including all handlers) when `journalItems` changes
table.current = useMemo(() => {
  return new Tabulator(refTable.current, {
        rowClick: function (e, row) {
            console.log("tabulator journalitems", journalItems)
            handleTableRowClick(row._row.data.id)
        },
        columns: [
            { title: "Компанија", field: "companyName" },
            { title: "Документ", field: "documentKey" },
        ],
    });
}, [journalItems]);

useEffect(() => {
    console.log("useEffect journalItems", journalItems) 
    table.current.replaceData(journalItems)              
}, [journalItems]);

const handleTableRowClick = useCallback((journalItemId) => {
  console.log("handletablerowclick journalitems", journalItems);
}, [journalItems]);

Don't forget to update the dependency array of your first effect. All variables from the outer scope that are referenced inside your effect must be in this array.

ChronoLink
  • 248
  • 3
  • 6
  • Thank you for your answer. Can you please check my solution and comment on it? Which approach is better? – Ivan Feb 22 '20 at 05:17