ловушка useState - состояние теряется, то есть сбрасывается до начального - PullRequest
0 голосов
/ 22 февраля 2020

Я пытаюсь инициализировать состояние во время цикла монтажа, а затем что-то делать с ним при каждом обновлении.

Однако состояние как-то сбрасывается? Я не понимаю, почему.

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 []
}

Результаты из журналов консоли ...

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

tabulator journalitems []
handleTableRowClick journalitems []

Ответы [ 2 ]

0 голосов
/ 22 февраля 2020

Я считаю, что это происходит потому, что начальное значение для journalItems закрывается внутри функции, определенной для свойства rowClick.

Поскольку эффект, содержащий table.current, запускается только один раз, это table.current значение получает все начальные значения и никогда не обновляется новыми обработчиками для rowClick проп.

Я подозреваю, что вам может потребоваться внести изменения, приведенные ниже, чтобы добиться прогресса:

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]);

Не забудьте обновить массив зависимостей вашего первого эффекта. Все переменные из внешней области видимости, на которые ссылается ваш эффект, должны быть в этом массиве.

0 голосов
/ 22 февраля 2020

Мне удалось решить это странным образом с помощью useRef для переменной функции и переместить определение функции в 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]);
...