Метод setState, не отражающий изменения немедленно

Я пытаюсь изучить хуки, а метод useState меня запутал. Я присваиваю начальное значение состоянию в виде массива. Метод set в useState у меня не работает даже с spread(...) или же without spread operator, Я сделал API на другом компьютере, который я вызываю и извлекаю данные, которые я хочу установить в состояние.

Вот мой код:

import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";

const StateSelector = () => {
  const initialValue = [
    {
      category: "",
      photo: "",
      description: "",
      id: 0,
      name: "",
      rating: 0
    }
  ];

  const [movies, setMovies] = useState(initialValue);

  useEffect(() => {
    (async function() {
      try {
        //const response = await fetch(
        //`http://192.168.1.164:5000/movies/display`
        //);
        //const json = await response.json();
        //const result = json.data.result;
        const result = [
          {
            category: "cat1",
            description: "desc1",
            id: "1546514491119",
            name: "randomname2",
            photo: null,
            rating: "3"
          },
          {
            category: "cat2",
            description: "desc1",
            id: "1546837819818",
            name: "randomname1",
            rating: "5"
          }
        ];
        console.log(result);
        setMovies(result);
        console.log(movies);
      } catch (e) {
        console.error(e);
      }
    })();
  }, []);

  return <p>hello</p>;
};

const rootElement = document.getElementById("root");
ReactDOM.render(<StateSelector />, rootElement);

setMovies(result) так же как setMovies(...result) не работает. Могли бы использовать некоторую помощь здесь. Заранее спасибо.

Я ожидаю, что переменная результата будет помещена в массив фильмов.

24 ответа

Решение

Очень похоже на setState в компонентах Class, созданных путем расширения React.Component или же React.PureComponentобновление состояния с использованием средства обновления, предоставленного useState ловушка также асинхронна и не будет немедленно отражать и обновлять, но вызовет повторную визуализацию

setMovies(result);
console.log(movies) // movies here will not be updated

Если вы хотите выполнить действие по обновлению состояния, вам нужно использовать хук useEffect, очень похожий на componentDidUpdate в компонентах класса, так как установщик, возвращаемый useState, не имеет шаблона обратного вызова

useEffect(() => {
    // action on update of movies
}, [movies]);

Что касается синтаксиса для обновления состояния, setMovies(result) заменит предыдущий movies значение в состоянии с теми, которые доступны из асинхронного запроса

Однако, если вы хотите объединить ответ с ранее существовавшими значениями, вы должны использовать синтаксис обратного вызова для обновления состояния вместе с правильным использованием синтаксиса распространения, такого как

setMovies(prevMovies => ([...prevMovies, ...result]));

Дополнительные детали к предыдущему ответу:

Хотя React's setState является асинхронным (и классы, и хуки), и заманчиво использовать этот факт для объяснения наблюдаемого поведения, но это не причина, по которой это происходит.

TL; DR: причина в закрытии области вокруг неизменяемогоconst ценность.


Решения:

  • прочтите значение в функции рендеринга (не внутри вложенных функций):

    useEffect(() => { setMovies(result) }, [])
    console.log(movies)
    
  • добавить переменную в зависимости (и использовать правило eslint react -hooks / excustive -deps):

    useEffect(() => { setMovies(result) }, [])
    useEffect(() => { console.log(movies) }, [movies])
    
  • используйте изменяемую ссылку (когда это невозможно):

    const moviesRef = useRef(initialValue)
    useEffect(() => {
      moviesRef.current = result
      console.log(moviesRef.current)
    }, [])
    

Объяснение, почему это происходит:

Если бы асинхронность была единственной причиной, можно было бы await setState().

Хауэрвер, оба props а также stateсчитаются неизменными в течение 1 рендера.

Лечить this.state как если бы он был неизменным.

В случае хуков это предположение усиливается за счет использования постоянных значений сconst ключевое слово:

const [state, setState] = useState('initial')

Значение может отличаться между двумя рендерами, но остается постоянным внутри самого рендеринга и внутри любых замыканий (функции, которые живут дольше даже после завершения рендеринга, напримерuseEffect, обработчики событий внутри любого Promise или setTimeout).

Рассмотрим следующую фальшивую, но синхронную реализацию, похожую на React:

// sync implementation:

let internalState
let renderAgain

const setState = (updateFn) => {
  internalState = updateFn(internalState)
  renderAgain()
}

const useState = (defaultState) => {
  if (!internalState) {
    internalState = defaultState
  }
  return [internalState, setState]
}

const render = (component, node) => {
  const {html, handleClick} = component()
  node.innerHTML = html
  renderAgain = () => render(component, node)
  return handleClick
}

// test:

const MyComponent = () => {
  const [x, setX] = useState(1)
  console.log('in render:', x) // ✅
  
  const handleClick = () => {
    setX(current => current + 1)
    console.log('in handler/effect/Promise/setTimeout:', x) // ❌ NOT updated
  }
  
  return {
    html: `<button>${x}</button>`,
    handleClick
  }
}

const triggerClick = render(MyComponent, document.getElementById('root'))
triggerClick()
triggerClick()
triggerClick()
<div id="root"></div>

Вы можете решить эту проблему, используя useRefхук, но тогда он не будет повторно отображаться при обновлении. Я создал хуки под названием useStateRef, которые дают вам пользу из обоих миров. Это похоже на состояние, при котором при обновлении Компонент повторно отрисовывается, и это похоже на «ref», у которого всегда есть последнее значение.

См. Этот пример:

      var [state,setState,ref]=useStateRef(0)

Он работает точно так же, как useState но, кроме того, он дает вам текущее состояние под ref.current

Узнать больше:

Я только что закончил перезапись с помощью useReducer, следуя статье @kentcdobs (см. Ниже), которая действительно дала мне твердый результат, который не страдает от этих проблем с закрытием.

см. https://kentcdodds.com/blog/how-to-use-react-context-effectively

Я сжал его читаемый шаблон до моего предпочтительного уровня СУХОСТИ - чтение его реализации в песочнице покажет вам, как это на самом деле работает.

Наслаждайтесь, я знаю!!

import React from 'react'

// ref: https://kentcdodds.com/blog/how-to-use-react-context-effectively

const ApplicationDispatch = React.createContext()
const ApplicationContext = React.createContext()

function stateReducer(state, action) {
  if (state.hasOwnProperty(action.type)) {
    return { ...state, [action.type]: state[action.type] = action.newValue };
  }
  throw new Error(`Unhandled action type: ${action.type}`);
}

const initialState = {
  keyCode: '',
  testCode: '',
  testMode: false,
  phoneNumber: '',
  resultCode: null,
  mobileInfo: '',
  configName: '',
  appConfig: {},
};

function DispatchProvider({ children }) {
  const [state, dispatch] = React.useReducer(stateReducer, initialState);
  return (
    <ApplicationDispatch.Provider value={dispatch}>
      <ApplicationContext.Provider value={state}>
        {children}
      </ApplicationContext.Provider>
    </ApplicationDispatch.Provider>
  )
}

function useDispatchable(stateName) {
  const context = React.useContext(ApplicationContext);
  const dispatch = React.useContext(ApplicationDispatch);
  return [context[stateName], newValue => dispatch({ type: stateName, newValue })];
}

function useKeyCode() { return useDispatchable('keyCode'); }
function useTestCode() { return useDispatchable('testCode'); }
function useTestMode() { return useDispatchable('testMode'); }
function usePhoneNumber() { return useDispatchable('phoneNumber'); }
function useResultCode() { return useDispatchable('resultCode'); }
function useMobileInfo() { return useDispatchable('mobileInfo'); }
function useConfigName() { return useDispatchable('configName'); }
function useAppConfig() { return useDispatchable('appConfig'); }

export {
  DispatchProvider,
  useKeyCode,
  useTestCode,
  useTestMode,
  usePhoneNumber,
  useResultCode,
  useMobileInfo,
  useConfigName,
  useAppConfig,
}

с использованием, подобным этому:

import { useHistory } from "react-router-dom";

// https://react-bootstrap.github.io/components/alerts
import { Container, Row } from 'react-bootstrap';

import { useAppConfig, useKeyCode, usePhoneNumber } from '../../ApplicationDispatchProvider';

import { ControlSet } from '../../components/control-set';
import { keypadClass } from '../../utils/style-utils';
import { MaskedEntry } from '../../components/masked-entry';
import { Messaging } from '../../components/messaging';
import { SimpleKeypad, HandleKeyPress, ALT_ID } from '../../components/simple-keypad';

export const AltIdPage = () => {
  const history = useHistory();
  const [keyCode, setKeyCode] = useKeyCode();
  const [phoneNumber, setPhoneNumber] = usePhoneNumber();
  const [appConfig, setAppConfig] = useAppConfig();

  const keyPressed = btn => {
    const maxLen = appConfig.phoneNumberEntry.entryLen;
    const newValue = HandleKeyPress(btn, phoneNumber).slice(0, maxLen);
    setPhoneNumber(newValue);
  }

  const doSubmit = () => {
    history.push('s');
  }

  const disableBtns = phoneNumber.length < appConfig.phoneNumberEntry.entryLen;

  return (
    <Container fluid className="text-center">
      <Row>
        <Messaging {...{ msgColors: appConfig.pageColors, msgLines: appConfig.entryMsgs.altIdMsgs }} />
      </Row>
      <Row>
        <MaskedEntry {...{ ...appConfig.phoneNumberEntry, entryColors: appConfig.pageColors, entryLine: phoneNumber }} />
      </Row>
      <Row>
        <SimpleKeypad {...{ keyboardName: ALT_ID, themeName: appConfig.keyTheme, keyPressed, styleClass: keypadClass }} />
      </Row>
      <Row>
        <ControlSet {...{ btnColors: appConfig.buttonColors, disabled: disableBtns, btns: [{ text: 'Submit', click: doSubmit }] }} />
      </Row>
    </Container>
  );
};

AltIdPage.propTypes = {};

Теперь все плавно сохраняется на всех моих страницах

Ницца!

Спасибо, Кент!

Use Effect имеет собственное состояние / жизненный цикл, он не будет обновляться, пока вы не передадите функцию в параметрах или эффект не будет уничтожен.

объект и массив распространение или отдых не будут работать внутри useEffect.

React.useEffect(() => {
    console.log("effect");
    (async () => {
        try {
            let result = await fetch("/query/countries");
            const res = await result.json();
            let result1 = await fetch("/query/projects");
            const res1 = await result1.json();
            let result11 = await fetch("/query/regions");
            const res11 = await result11.json();
            setData({
                countries: res,
                projects: res1,
                regions: res11
            });
        } catch {}
    })(data)
}, [setData])
# or use this
useEffect(() => {
    (async () => {
        try {
            await Promise.all([
                fetch("/query/countries").then((response) => response.json()),
                fetch("/query/projects").then((response) => response.json()),
                fetch("/query/regions").then((response) => response.json())
            ]).then(([country, project, region]) => {
                // console.log(country, project, region);
                setData({
                    countries: country,
                    projects: project,
                    regions: region
                });
            })
        } catch {
            console.log("data fetch error")
        }
    })()
}, [setData]);

Я тоже застрял с той же проблемой. Поскольку другие ответы выше разъяснили здесь ошибку, которая заключается в том, чтоuseStateявляется асинхронным, и вы пытаетесь использовать значение сразу после . Он не обновляется наconsole.log()часть из-за асинхронного характера позволяет выполнять ваш дальнейший код, в то время как обновление значения происходит в фоновом режиме. Таким образом, вы получаете предыдущее значение. КогдаsetStateзавершено на фоне, оно обновит значение, и у вас будет доступ к этому значению при следующем рендеринге.

Если кому интересно разобраться в этом подробнее. Вот действительно хороший доклад на конференции по этой теме.

https://www.youtube.com/watch?v=8aGhZQkoFbQ

Большинство ответов здесь о том, как обновить состояние на основе его предыдущего значения, но я не понимаю, как это связано с вопросом.

Метод setState не отражает изменение немедленно.


Реагировать 18

useState является асинхронным:

Когда происходит событие, которое запускает определенный код, код начинает выполняться, и когда он завершается, реакция проверит, было ли обновление состояния, и если это так, только тогда значение перехватчика обновляется, и это приводит к новый рендеринг, в котором доступно новое значение.

      const [example,setExemple] = useState("")
//...
<button
  onClick={() => {
    const newValue = "new";
    setExample(newValue);
    console.log(example); // output "" and this is normal, because the component didn't rerenderd yet so the new value is not availabe yet
  }}
>
  Update state
</button>

Предположим, у нас есть сценарий, в котором у нас есть состояние, которое зависит от другого состояния, например, мы хотим сделать вызов API на основе нового значения каждый раз, когда оно обновляется, а затем сохранить данные из ответа в другом состоянии.
Для этого у нас есть два пути:

1. используйте значение:

      <button
  onClick={async () => {
    const newValue = "new";
    const response = await axios.get(`http://127.0.0.1:5000/${newValue}`);
    setExample(newValue);
    setAnotherExample(response.data);
  }}
>
  test
</button>

поскольку вы знаете, что получите это значение, вы можете напрямую создать свою логику на его основе.

2. запускать useEffect при каждом обновлении, включая в его массив зависимостей:

      <button
  onClick={() => {
    const newValue = "new";
    setExample(newValue);
  }}
>
  test
</button>
      useEffect(() => {
 async function test(){
  const response = await axios.get(`http://127.0.0.1:5000/${example}`);
  setAnotherExample(response.data);
 } 
 test();
}, [example])

поэтому, когда обновляется функция события, которую компонент повторно отображает, мы теперь находимся в новом другом рендеринге , который после завершения будет запущен, поскольку значение отличается от того, что было во время последнего рендеринга , и поскольку это новый другой рендеринг, новое значение хука useState доступно здесь.

Примечание: хук в любом случае будет работать во время первого монтирования.

Какой подход лучше?

  • в то время как первый метод выполнит всю работу за один рендеринг (лучший подход) «React группирует несколько обновлений состояния в один повторный рендеринг для повышения производительности», второй метод выполнит это за два рендеринга: первый при обновлении и второй второй, когда обновляется изнутри

  • поскольку компонент выполняет повторную визуализацию только тогда, когда новое значение перехватчика отличается от старого, поэтому, когда равно компоненту, не будет выполняться повторная визуализация, поэтомуне будет запускаться и не будет обновляться (лучший подход) , однако в первом методе API все равно вызывается, и мы не хотим этого делать, если в этом нет необходимости, и если это произойдет, оно будет обновлено (получит те же данные, которые уже содержит, поскольку это тот же ЗАПРОС, поскольку он равен), но если ответ в объекте или массиве, тометод (чтоперехватчик usezs), не может определить, равно ли новое значение предыдущему, поэтому компонент будет перерисовываться

Заключение:

Как упоминалось выше, у каждого из них есть свои преимущества, поэтому все зависит от варианта использования.

второй метод более рекомендуется , однако в некоторых случаях первый может быть более производительным, например, если вы уверены, что код будет запускаться только тогда, когдаполучает новое значение, используяили, может быть, когда вы захотите использовать некоторые другие локальные переменные, к которым у вас больше не будет доступа изнутри useEffect

Закрытие - не единственная причина.

На основе исходного кода (упрощенного ниже). Мне кажется, значение никогда не назначается сразу.

Что происходит, так это то, что действие обновления ставится в очередь, когда вы вызываете setValue. И после того, как расписание сработает, и только когда вы перейдете к следующему рендерингу, это действие обновления применяется к этому состоянию.

Это означает, что даже у нас нет проблемы с закрытием, реагируйте на версию useStateне сразу даст вам новое значение. Новое значение даже не существует до следующего рендеринга.

        function useState(initialState) {
    let hook;
    ...

    let baseState = hook.memoizedState;
    if (hook.queue.pending) {
      let firstUpdate = hook.queue.pending.next;

      do {
        const action = firstUpdate.action;
        baseState = action(baseState);            // setValue HERE
        firstUpdate = firstUpdate.next;
      } while (firstUpdate !== hook.queue.pending);

      hook.queue.pending = null;
    }
    hook.memoizedState = baseState;

    return [baseState, dispatchAction.bind(null, hook.queue)];
  }

function dispatchAction(queue, action) {
  const update = {
    action,
    next: null
  };
  if (queue.pending === null) {
    update.next = update;
  } else {
    update.next = queue.pending.next;
    queue.pending.next = update;
  }
  queue.pending = update;

  isMount = false;
  workInProgressHook = fiber.memoizedState;
  schedule();
}

Также есть статья, объясняющая вышесказанное аналогичным образом, https://dev.to/adamklein/we-don-t-know-how-react-state-hook-works-1lp8

Функция setState , возвращаемая перехватчиком useState в React, не обновляет состояние немедленно. Вместо этого он планирует обработку обновления состояния в следующем цикле рендеринга. Это связано с тем, что React пакетно обновляет состояние из соображений производительности.

Если вы пытаетесь получить доступ к обновленному состоянию сразу после вызова setState , вы можете не увидеть обновленное значение сразу. Вместо этого вы можете использовать перехватчик useEffect для выполнения действий после обновления состояния.

Вот пример, демонстрирующий, как вы можете использовать useEffect для выполнения действия после обновления состояния.

       import React, { useState, useEffect } from 'react';

const MyComponent = () => {
  const [count, setCount] = useState(0);

  useEffect(() => {
    // This effect will run after each state update
    console.log('Count has been updated:', count);
  }, [count]);

  const incrementCount = () => {
    setCount(count + 1);
  };

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={incrementCount}>Increment</button>
    </div>
  );
};

В приведенном выше примере перехват useEffect используется для регистрации обновленного значения счетчика после каждого обновления состояния. Если передать [count] в качестве массива зависимостей в useEffect , эффект будет запускаться только при изменении состояния счетчика .

Я считаю, что это хорошо, вместо того, чтобы определять состояние (подход 1), например,

const initialValue = 1;

const [state,setState] = useState(initialValue)

Попробуйте этот подход (подход 2),

const [state = initialValue,setState] = useState()

Это решило проблему повторного рендеринга без использования useEffect, поскольку в этом случае нас не интересует его подход к внутреннему закрытию.

PS Если вас беспокоит использование старого состояния для любого варианта использования, тогда необходимо использовать useState с useEffect, так как оно должно иметь это состояние, поэтому в этой ситуации должен использоваться подход 1.

Есть много хороших ответов, которые показывают, как исправить ваш код, но есть пакет NPM, который позволяет вам исправить это, просто изменив . это называется response-useStateRef

В твоем случае:

      import useState from 'react-usestateref'
const [movies, setMovies,moviesRef] = useState(initialValue);
....
useEffect(() => {
   setMovies(...)
   console.log(moviesRef.current) // it will have the last value
})

Как вы видете. Используя эту библиотеку, вы можете получить доступ к последнему состоянию.

если нам нужно только обновить состояние, лучшим способом будет использование метода push. Вот мой код, в котором я хочу хранить URL-адреса из firebase в состоянии.

        const [imageUrl, setImageUrl] = useState([]);
 const [reload, setReload] = useState(0);

useEffect(() => {
if (reload === 4) {
  downloadUrl1();
}
}, [reload]);



const downloadUrl = async () => {
setImages([]);
try {
  for (let i = 0; i < images.length; i++) {
    let url = await storage().ref(urls[i].path).getDownloadURL();
    imageUrl.push(url);
    setImageUrl([...imageUrl]);

    console.log(url, 'check', urls.length, 'length', imageUrl.length);
  }
} catch (e) {
  console.log(e);
}
  };
 const handleSubmit = async () => {
  setReload(4);
  await downloadUrl();
  console.log(imageUrl);
  console.log('post submitted');
  };

Этот код работает, чтобы поместить URL-адреса в состояние как массив. Это может сработать и для вас

var [состояние,setState] = useState (defaultValue)

useEffect (() =>{var updatedState setState (currentState =>{// Не изменять состояние, получая обновленное состояние updateState = currentState return currentState})alert (updateState) // текущее состояние.})

⚠️ Функциональные компоненты

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

Прочитав этот пост, вы поймете, как сделать крючок. Определение состояния с помощьюuseStateCallbackбудет выглядеть следующим образом:

      const [count, setCount] = useStateCallback(0);

const handleFooBar = () => {
  setCount(c => c + 1, () => { // The callback function
    // All actions here will be run exactly AFTER the update of the count state
  })
};

Проблема

На самом деле моя проблема заключалась не в попытке получить доступ к состоянию сразу после вызова метода set. Я пытался сделать это в совершенно другой функции после того, как произошел повторный рендеринг, но обновление все еще не отражалось. Целевая функция была определена в функциональном компоненте, но вызывалась из компонента класса.

В моем случае я понял, что это проблема, вызванная устаревшим закрытием. Вероятно, это произошло потому, что компоненты класса не используютuseStateфункциональность, и поэтому компонент класса в моем коде взял переданную ему функцию и создал ее копию или что-то в этом роде, и эта копия не использовала самую последнюю ссылку на мою переменную. Однако фактические переменные, переданные непосредственно в компонент класса, по-прежнему отображались правильно.

Решение

Замена компонента класса функциональным компонентом решила для меня проблему.

Я нашел уловку в setStateкрюк. Вы не должны использовать старую переменную. Вы должны создать новую переменную и передать ее на перехватчик. Например:

      const [users, setUsers] = useState(['Ayşe', 'Fatma'])

useEffect(() => {
    setUsers((oldUsers) => {
        oldUsers.push(<div>Emir</div>)
        oldUsers.push(<div>Buğra</div>)
        oldUsers.push(<div>Emre</div>)
        return oldUsers
    })
}, [])

return (
    <Fragment>
        {users}
    </Fragment>
)

Вы увидите только Ayşe а также Fatmaпользователей. Потому что вы возвращаетесь (или проезжаете) oldUsersПеременная. Эта переменная имеет ТАКУЮ ССЫЛКУ со ссылкой на старое состояние. Вы должны вернуть НОВОЕ СОЗДАННУЮ переменную. Если вы передадите ту же указанную переменную, тогда Reactjs не обновит состояние.

      const [users, setUsers] = useState(['Ayşe', 'Fatma'])

useEffect(() => {
    setUsers((oldUsers) => {
        const newUsers = [] // Create new array. This is so important.
        
        // you must push every old item to our newly created array
        oldUsers.map((user, index) => {
            newUsers.push(user)
        })
        // NOTE: map() function is synchronous
        
        newUsers.push(<div>Emir</div>)
        newUsers.push(<div>Buğra</div>)
        newUsers.push(<div>Emre</div>)
        return newUsers
    })
}, [])

return (
    <Fragment>
        {users}
    </Fragment>
)
      setMovies(result);

setMovies((state)=>{
console.log(state);
return state;
});

setState - это асинхронный метод, чтобы проверить новое состояние, вам нужно добавить функцию обратного вызова или добавить async / await

пример:

      this.setState({
   value: newValue
},()=>{
   // It is an callback function.
   // Here you can access the update value
   console.log(this.state.value)
})

или же

         await this.setState({value: newValue})
   console.log(this.state.value)

Несложно реализовать обратный вызов установки состояния (без useEffect) в области действия функции установки.

Используйте обещание для разрешения нового состояния в теле функции установки:

      const getState = <T>(
  setState: React.Dispatch<React.SetStateAction<T>>
): Promise<T> => {
  return new Promise((resolve) => {
    setState((currentState: T) => {
      resolve(currentState);
      return currentState;
    });
  });
};

В этом примере показано сравнение между countа также outOfSyncCount/ syncCountв рендеринге пользовательского интерфейса:

      const App: React.FC = () => {
  const [count, setCount] = useState(0);
  const [outOfSyncCount, setOutOfSyncCount] = useState(0);
  const [syncCount, setSyncCount] = useState(0);

  const handleOnClick = async () => {
    setCount(count + 1);
    setOutOfSyncCount(count);
    const newCount = await getState(setCount);
    setSyncCount(newCount);
    // Or then'able
    // getState(setCount).then(setSyncCount);
  };

  return (
    <>
      <h2>Count = {count}</h2>
      <h2>Synced count = {syncCount}</h2>
      <h2>Out of sync count = {outOfSyncCount}</h2>
      <button onClick={handleOnClick}>Increment</button>
    </>
  );
};

Я думаю, что привлекательным вариантом использования этого подхода является контекстный API и асинхронные действия и/или действия с побочными эффектами, которые очень напоминают преобразователи Redux. Это потребует меньшей сложности, чем метод Кента . (Я не использовал это в больших примерах или производстве, поэтому я не знаю, может быть, вы можете попасть в какие-то странные состояния).

Пример:

      // Sample "thunk" with async and side effects
const handleSubmit = async (
  setState: React.Dispatch<SetStateAction<FormState>>
): Promise<void> => {
  const state = await getState(setState);
​
  if (!state.validated) {
    return;
  }
​
  // Submit endpoint
  await fetch('submitFormUrl', { method: 'post', body: state });
  tackFormSuccess(state);
  setState({ ...state, submitted: true });
};
​
// Component consuming the thunk
const SubmitComponent: React.FC = () => {
  const dispatch = useFormDispatch();
  return <button onclick={() => handleSubmit(dispatch)}>Submit</button>
}

используйте библиотеку фонового таймера, чтобы решить мою проблему https://github.com/ocetnik/react-native-background-timer

const timeoutId = BackgroundTimer.setTimeout(() => { // this will be executed once after 1 seconds // even when app is the the background console.log('tac'); }, 1000);

Для более удобной работы с обещаниями существует специальный синтаксис, который называется «async / await». Его на удивление легко понять и использовать.

В этом случае с setState может быть несколько решений. Одно из самых простых и удобных решений, которое я нашел, заключается в следующем: в функциональном компоненте используйте useEffect() как async / await. т.е. здесь, в примере выше, useEffect() уже выполняется как асинхронная функция. Теперь сделайте setMovies(results) как await, например:

      await setMovies(results);

Это обязательно решит проблему немедленных изменений. Ключевое слово await заставляет JavaScript ждать, пока это обещание не будет выполнено, и вернет свой результат. Вам также не нужно устанавливать начальное значение, как написано в вопросе выше. Вы можете объявлять только переменные фильмы, например

      const [movies, setMovies] = useState([]);

больше информации: https://javascript.info/async-await

С настраиваемыми хуками из моей библиотеки вы можете дождаться обновления значений состояния:

  1. useAsyncWatcher(...values):watcherFn(peekPrevValue: boolean)=>Promise - это оболочка обещания вокруг useEffect, которая может ждать обновлений и возвращать новое значение и, возможно, предыдущее, если необязательный peekPrevValue аргумент имеет значение true.

(Живая демонстрация )

          import React, { useState, useEffect, useCallback } from "react";
    import { useAsyncWatcher } from "use-async-effect2";
    
    function TestComponent(props) {
      const [counter, setCounter] = useState(0);
      const [text, setText] = useState("");
    
      const textWatcher = useAsyncWatcher(text);
    
      useEffect(() => {
        setText(`Counter: ${counter}`);
      }, [counter]);
    
      const inc = useCallback(() => {
        (async () => {
          await new Promise((resolve) => setTimeout(resolve, 1000));
          setCounter((counter) => counter + 1);
          const updatedText = await textWatcher();
          console.log(updatedText);
        })();
      }, []);
    
      return (
        <div className="component">
          <div className="caption">useAsyncEffect demo</div>
          <div>{counter}</div>
          <button onClick={inc}>Inc counter</button>
        </div>
      );
    }
    
    export default TestComponent;
  1. useAsyncDeepState- это реализация с глубоким состоянием (похожая на this.setState (patchObject)), сеттер которой может возвращать обещание, синхронизированное с внутренним эффектом. Если сеттер вызывается без аргументов, он не изменяет значения состояния, а просто подписывается на обновления состояния. В этом случае вы можете получить значение состояния из любого места внутри вашего компонента, поскольку закрытие функций больше не является помехой.

(Живая демонстрация )

      import React, { useCallback, useEffect } from "react";
import { useAsyncDeepState } from "use-async-effect2";

function TestComponent(props) {
  const [state, setState] = useAsyncDeepState({
    counter: 0,
    computedCounter: 0
  });

  useEffect(() => {
    setState(({ counter }) => ({
      computedCounter: counter * 2
    }));
  }, [state.counter]);

  const inc = useCallback(() => {
    (async () => {
      await new Promise((resolve) => setTimeout(resolve, 1000));
      await setState(({ counter }) => ({ counter: counter + 1 }));
      console.log("computedCounter=", state.computedCounter);
    })();
  });

  return (
    <div className="component">
      <div className="caption">useAsyncDeepState demo</div>
      <div>state.counter : {state.counter}</div>
      <div>state.computedCounter : {state.computedCounter}</div>
      <button onClick={() => inc()}>Inc counter</button>
    </div>
  );
}
// replace
return <p>hello</p>;
// with
return <p>{JSON.stringify(movies)}</p>;

Теперь вы должны увидеть, что ваш код на самом деле делает работу. Что не работает, так этоconsole.log(movies). Это потому чтоmoviesуказывает на старое состояние. Если вы переместитеconsole.log(movies) вне useEffect, прямо над возвратом вы увидите обновленный объект фильмов.

Без какого-либо дополнительного пакета NPM

      //...
const BackendPageListing = () => {
    
    const [ myData, setMyData] = useState( {
        id: 1,
        content: "abc"
    })

    const myFunction = ( x ) => {
        
        setPagenateInfo({
        ...myData,
        content: x
        })

        console.log(myData) // not reflecting change immediately

        let myDataNew = {...myData, content: x };
        
        console.log(myDataNew) // Reflecting change immediately

    }

    return (
        <>
            <button onClick={()=>{ myFunction("New Content")} }>Update MyData</button>
        </>
    )
Другие вопросы по тегам