我正在尝试学习钩子和useState方法使我困惑。我以数组的形式将初始值赋给一个状态。useState中的set方法不适合我,无论是否使用扩展语法。

我已经在另一台PC上做了一个API,我正在调用并获取我想设置为状态的数据。

这是我的代码:

<div id="root"></div> <script type="text/babel" defer> // import React, { useState, useEffect } from "react"; // import ReactDOM from "react-dom"; const { useState, useEffect } = React; // web-browser variant 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 =", result); setMovies(result); console.log("movies =", movies); } catch (e) { console.error(e); } })(); }, []); return <p>hello</p>; }; const rootElement = document.getElementById("root"); ReactDOM.render(<StateSelector />, rootElement); </script> <script src="https://unpkg.com/@babel/standalone@7/babel.min.js"></script> <script src="https://unpkg.com/react@17/umd/react.production.min.js"></script> <script src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js"></script>

setMovies(result)和setMovies(…result)都不起作用。

我希望结果变量被推入movies数组。


当前回答

var [state,setState]=useState(defaultValue)

useEffect(()=>{
   var updatedState
   setState(currentState=>{    // Do not change the state by get the updated state
      updateState=currentState
      return currentState
   })
   alert(updateState) // the current state.
})

其他回答

I too was stuck with the same problem. As other answers above have clarified the error here, which is that useState is asynchronous and you are trying to use the value just after setState. It is not updating on the console.log() part because of the asynchronous nature of setState, it lets your further code to execute, while the value updating happens on the background. Thus you are getting the previous value. When the setState is completed on the background it will update the value and you will have access to that value on the next render.

如果有人有兴趣了解这个细节。这是一个关于这个话题的非常好的会议发言。

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

不需要任何额外的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>
        </>
    )

关闭并不是唯一的原因。

基于useState的源代码(下面进行了简化)。在我看来,价值从来没有被立即分配。

当您调用setValue时,更新操作将被排队。在时间表生效后,只有当你到达下一个渲染时,这些更新操作才会应用到那个状态。

这意味着即使我们没有闭包问题,useState的react版本也不会马上给你新值。新的值直到下一次渲染才会存在。

  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

如果我们必须只更新状态,那么更好的方法是使用push方法。

这是我的代码。我想从Firebase状态存储url。

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置于数组状态。这可能对你也有用。

我发现这很好。不将状态(方法1)定义为,例如,

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

试试这个方法(方法2),

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

这在没有使用useEffect的情况下解决了渲染问题,因为在这种情况下我们不关心它的内部闭包方法。

附注:如果你关心在任何用例中使用旧状态,那么useState和useEffect需要使用,因为它需要那种状态,所以在这种情况下接近1应使用。