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