Javascript useState集合方法不立即反映更改

Javascript useState集合方法不立即反映更改,javascript,reactjs,react-hooks,Javascript,Reactjs,React Hooks,我正在尝试学习挂钩,而useState方法让我感到困惑。我以数组的形式为状态赋值。useState中的set方法不适用于我,即使使用spread(…)或不使用spread操作符的。 我在另一台PC上制作了一个API,我正在调用它并获取我想要设置为状态的数据 这是我的密码: //从“React”导入React,{useState,useffect}; //从“react dom”导入react dom; 常量{useState,useffect}=React;//web浏览器变体 常量状态选择

我正在尝试学习挂钩,而
useState
方法让我感到困惑。我以数组的形式为状态赋值。
useState
中的set方法不适用于我,即使使用
spread(…)
或不使用spread操作符的
。
我在另一台PC上制作了一个API,我正在调用它并获取我想要设置为状态的数据

这是我的密码:


//从“React”导入React,{useState,useffect};
//从“react dom”导入react dom;
常量{useState,useffect}=React;//web浏览器变体
常量状态选择器=()=>{
常量初始值=[
{
类别:“,
照片:“,
说明:“,
id:0,
姓名:“,
评级:0
}
];
const[movies,setMovies]=useState(初始值);
useffect(()=>{
(异步函数(){
试一试{
//const response=等待获取(“http://192.168.1.164:5000/movies/display");
//const json=await response.json();
//const result=json.data.result;
常数结果=[
{
类别:“第1类”,
描述:“desc1”,
id:“1546514491119”,
名称:“随机名称2”,
照片:空,
评级:“3”
},
{
类别:“第二类”,
描述:“desc1”,
id:“1546837819818”,
名称:“随机名称1”,
评级:“5”
}
];
console.log(“result=”,result);
结果;
console.log(“movies=”,movies);
}捕获(e){
控制台错误(e);
}
})();
}, []);
return你好

; }; const rootElement=document.getElementById(“根”); render(,rootElement);
很像通过扩展
React.Component
React.PureComponent
创建的类组件中的setState,使用
useState
钩子提供的更新程序进行的状态更新也是异步的,不会立即反映出来

此外,这里的主要问题不仅仅是异步性质,还有一个事实,即状态值由函数根据其当前闭包使用,状态更新将反映在下一次重新呈现中,现有闭包不受影响,而是创建新的闭包。现在,在当前状态下,钩子中的值由现有闭包获得,当重新呈现时,闭包将根据是否重新创建函数进行更新

即使在函数中添加一个
setTimeout
,虽然超时将在重新渲染发生的一段时间后运行,
setTimeout
仍将使用其先前关闭的值,而不是更新的值

setMovies(result);
console.log(movies) // movies here will not be updated
如果要对状态更新执行操作,则需要使用useEffect钩子,就像在类组件中使用
componentDidUpdate
一样,因为useState返回的setter没有回调模式

useEffect(() => {
    // action on update of movies
}, [movies]);
就更新状态的语法而言,
setMovies(result)
将用异步请求中可用的值替换状态中以前的
movies

但是,如果要将响应与以前存在的值合并,则必须使用状态更新的回调语法以及正确使用扩展语法,如

setMovies(prevMovies => ([...prevMovies, ...result]));
其他详情如下:

虽然React的
setState
是异步的(类和钩子都是异步的),并且很容易用这个事实来解释观察到的行为,但这并不是它发生的原因

TLDR:原因是围绕不可变的
const
值的作用域


解决:
  • 读取渲染函数中的值(不在嵌套函数中):

  • 将变量添加到依赖项中(并使用eslint规则):

  • 使用可变引用(当无法执行上述操作时):


解释发生的原因: 如果async是唯一的原因,则可以
等待setState()

但是,
道具
状态
都是

此.state
视为是不可变的

对于hook,通过使用常量值
const
关键字来增强该假设:

const [state, setState] = useState('initial')
该值在两次渲染之间可能不同,但在渲染本身和任何渲染内部保持不变(即使在渲染完成后仍然有效的函数,例如
useffect
、事件处理程序、任何Promise或setTimeout内部)

考虑以下伪同步的、类似于实现的响应:

//同步实现:
让我们内化状态
让renderAgain
const setState=(updateFn)=>{
internalState=updateFn(internalState)
renderAgain()
}
const useState=(defaultState)=>{
如果(!internalState){
internalState=defaultState
}
返回[内部状态,设置状态]
}
常量渲染=(组件、节点)=>{
const{html,handleClick}=component()
node.innerHTML=html
renderAgain=()=>渲染(组件、节点)
回程手柄
}
//测试:
常量MyComponent=()=>{
常数[x,setX]=useState(1)
console.log('in render:',x)//✅
常量handleClick=()=>{
setX(电流=>电流+1)
log('in handler/effect/Promise/setTimeout:',x)//❌ 未更新
}
返回{
html:`${x}`,
手舔
}
}
const triggerClick=render(MyComponent,document.getElementById('root'))
triggerClick()
triggerClick()
triggerClick()
//替换
return你好

; //与 return{JSON.stringify(movies)}

现在您应该看到,您的代码实际上是工作的。不起作用的是
控制台.log(电影)
。这是因为
电影
指向旧状态。
  useEffect(() => { setMovies(result) }, [])
  useEffect(() => { console.log(movies) }, [movies])
  const moviesRef = useRef(initialValue)
  useEffect(() => {
    moviesRef.current = result
    console.log(moviesRef.current)
  }, [])
const [state, setState] = useState('initial')
// replace
return <p>hello</p>;
// with
return <p>{JSON.stringify(movies)}</p>;
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 = {};
import useState from 'react-usestateref'
const [movies, setMovies,moviesRef] = useState(initialValue);
....
useEffect(() => {
   setMovies(...)
   console.log(moviesRef.current) // it will have the last value
})
await setMovies(results);
const [movies, setMovies] = useState([]);
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)
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>
)
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>
)