Pra*_*jal 46 javascript reactjs react-hooks
我正在尝试学习钩子,而useState方法使我感到困惑。我正在将初始值分配给数组形式的状态。即使使用spread(...)
或,useState中的set方法对我也不起作用without spread operator
。我在另一台PC上制作了一个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);
Run Code Online (Sandbox Code Playgroud)
的setMovies(result)
以及setMovies(...result)
不工作。可以在这里使用一些帮助。提前致谢。
我希望将结果变量推入movies数组中。
Apr*_*ion 332
上一个答案的其他详细信息:
虽然 ReactsetState
是异步的(类和钩子),并且很容易用这个事实来解释观察到的行为,但这并不是它发生的原因。
TLDR:原因是围绕不可变值的闭包范围const
。
读取渲染函数中的值(不在嵌套函数内):
useEffect(() => { setMovies(result) }, [])
console.log(movies)
Run Code Online (Sandbox Code Playgroud)
将变量添加到依赖项中(并使用react-hooks/exhaustive-deps eslint 规则):
useEffect(() => { setMovies(result) }, [])
useEffect(() => { console.log(movies) }, [movies])
Run Code Online (Sandbox Code Playgroud)
使用可变引用(当上述不可能时):
const moviesRef = useRef(initialValue)
useEffect(() => {
moviesRef.current = result
console.log(moviesRef.current)
}, [])
Run Code Online (Sandbox Code Playgroud)
如果异步是唯一的原因,那么await setState()
.
但是,props
和state
都假定在 1 render 期间保持不变。
将其
this.state
视为不可变的。
使用钩子,通过使用带有关键字的常量值可以增强这种假设const
:
const [state, setState] = useState('initial')
Run Code Online (Sandbox Code Playgroud)
两次渲染之间的值可能不同,但在渲染本身和任何闭包(即使在渲染完成后存活时间更长的函数,例如useEffect
,任何 Promise 或 setTimeout 内的事件处理程序)内仍保持不变。
考虑以下虚假但同步的类似 React 的实现:
useEffect(() => { setMovies(result) }, [])
console.log(movies)
Run Code Online (Sandbox Code Playgroud)
useEffect(() => { setMovies(result) }, [])
useEffect(() => { console.log(movies) }, [movies])
Run Code Online (Sandbox Code Playgroud)
Shu*_*tri 78
类似于通过扩展React.Component
或创建的Class组件中的setState,React.PureComponent
使用useState
钩子提供的更新程序的状态更新也是异步的,不会立即反映和更新,但会触发重新渲染
setMovies(result);
console.log(movies) // movies here will not be updated
Run Code Online (Sandbox Code Playgroud)
如果要对状态更新执行操作,则需要使用useEffect钩子,就像componentDidUpdate
在类组件中使用一样,因为useState返回的setter没有回调模式
useEffect(() => {
// action on update of movies
}, [movies]);
Run Code Online (Sandbox Code Playgroud)
就更新状态的语法而言,setMovies(result)
将状态中的先前movies
值替换为异步请求中可用的值
但是,如果要将响应与先前存在的值合并,则必须使用状态更新的回调语法以及正确使用的扩展语法,例如
setMovies(prevMovies => ([...prevMovies, ...result]));
Run Code Online (Sandbox Code Playgroud)
Muh*_*que 13
React 的 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]);
Run Code Online (Sandbox Code Playgroud)
或者,您可以尝试 React.useRef() 在 React hook 中进行即时更改。
const movies = React.useRef(null);
useEffect(() => {
movies.current='values';
console.log(movies.current)
}, [])
Run Code Online (Sandbox Code Playgroud)
小智 12
我也遇到了同样的问题。正如上面的其他答案已经澄清了这里的错误,这是useState
异步的,您正在尝试使用setState
. console.log()
由于 的异步性质,它不会在部件上更新setState
,它允许您执行进一步的代码,而值更新发生在后台。这样你就得到了之前的值。当setState
在后台完成时,它将更新该值,您将可以在下一次渲染时访问该值。
如果有人有兴趣详细了解这一点。这是关于该主题的非常好的会议演讲。
https://www.youtube.com/watch?v=8aGhZQkoFbQ
Ahm*_*bai 12
这里的大多数答案都是关于如何根据其先前的值更新状态,但我不明白这与问题有何关系
useState set 方法没有立即反映更改
当触发某个代码的事件发生时,代码开始运行,当它完成时,react将检查是否有状态更新,如果是,则只有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>
Run Code Online (Sandbox Code Playgroud)
假设我们有一个场景,一个状态依赖于另一个状态,例如我们希望根据example
每次更新时的新值进行 API 调用,然后将响应中的数据存储在另一个状态中anotherExample
。
为了实现这样我们有两种方法:
1. 使用 的值newValue
:
<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>
Run Code Online (Sandbox Code Playgroud)
因为您知道example
将收到该值,所以您可以直接基于它创建逻辑。
2.每次更新时触发useEffect运行,方法是将其包含在其依赖项数组中:example
example
<button
onClick={() => {
const newValue = "new";
setExample(newValue);
}}
>
test
</button>
Run Code Online (Sandbox Code Playgroud)
useEffect(() => {
async function test(){
const response = await axios.get(`http://127.0.0.1:5000/${example}`);
setAnotherExample(response.data);
}
test();
}, [example])
Run Code Online (Sandbox Code Playgroud)
因此,当example
使用事件函数更新组件重新渲染时,我们现在处于一个新的不同渲染中,一旦完成,useEffect
就会运行,因为 的值example
与上次渲染期间的值不同,并且因为它是一个新的不同渲染, useState 挂钩的新值example
可在此处获得。
注意:useEffect
在第一次安装期间,钩子无论如何都会运行。
第一种方法将在一次渲染中完成所有工作 (更好的方法) “React 将多个状态更新分组到单个重新渲染中以获得更好的性能”,第二种方法将在两次渲染中完成,第一次example
更新时和第二次anotherExample
从内部更新useEffect
由于组件仅在useState
钩子的新值与旧值不同时才重新渲染,因此当newValue
等于时example
组件不会重新渲染,因此useEffect
不会运行且anotherExample
不会更新 (更好的方法),但是在第一种方法中无论如何,API 都会被调用,如果不需要,我们不想这样做,如果发生这种情况,anotherExample
也会更新(anotherExample
将收到它已经包含的相同数据,因为它是相同的请求,因为newValue
等于example
),但如果响应在对象或数组中,Object.is
方法(useState
钩子使用的)无法检测新值是否等于前一个值,因此,组件将重新渲染
正如上面提到的,每种都有其优点,所以这取决于用例。
更推荐使用第二种方法,但是在某些情况下,第一种方法的性能更高,例如,当您确定代码仅在newValue
使用 获取新值时才运行onChange
,或者当您想使用您将要使用的其他一些局部变量时不再能够从 useEffect 内部访问
win*_*mao 11
关闭并不是唯一的原因。
基于useState
(下面简化)的源代码。在我看来,该值永远不会立即分配。
发生的情况是,当您调用 时,更新操作会排队setValue
。在计划开始后,只有当您到达下一个渲染时,这些更新操作才会应用于该状态。
这意味着即使我们没有关闭问题,react 版本也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();
}
Run Code Online (Sandbox Code Playgroud)
还有一篇文章以类似的方式解释了上述内容,https://dev.to/adamklein/we-don-t-know-how-react-state-hook-works-1lp8
Ami*_*ein 10
我知道已经有很好的答案了。但我想给出另一个想法如何解决相同的问题,并使用我的模块react-useStateRef访问最新的“电影”状态
正如您所了解的,通过使用 React 状态,您可以在每次状态更改时渲染页面。但是通过使用 React ref,你总是可以获得最新的值。
所以这个模块react-useStateRef
让你可以一起使用 state 和 ref。它具有向后兼容性,React.useState
因此您只需替换import
语句
const { useEffect } = React
import { useState } from 'react-usestateref'
const [movies, setMovies] = useState(initialValue);
useEffect(() => {
(async function() {
try {
const result = [
{
id: "1546514491119",
},
];
console.log("result =", result);
setMovies(result);
console.log("movies =", movies.current); // will give you the latest results
} catch (e) {
console.error(e);
}
})();
}, []);
Run Code Online (Sandbox Code Playgroud)
我刚刚完成了使用 useReducer 的重写,遵循 @kentcdobs 文章(参考下面),这确实给了我一个可靠的结果,并且不受这些闭包问题的影响。
见:https : //kentcdodds.com/blog/how-to-use-react-context-effectively
我将他的可读样板压缩到我喜欢的 DRYness 级别——阅读他的沙箱实现将向您展示它是如何实际工作的。
享受,我知道我是!
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,
}
Run Code Online (Sandbox Code Playgroud)
用法与此类似:
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 = {};
Run Code Online (Sandbox Code Playgroud)
现在一切都在我所有的页面上顺利地持续存在
好的!
谢谢肯特!
React 中useState hook返回的setState函数不会立即更新状态。相反,它会安排在下一个渲染周期中处理状态更新。这是因为 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>
);
};
Run Code Online (Sandbox Code Playgroud)
在上面的示例中,useEffect挂钩用于记录每次状态更新后更新的计数值。通过将[count]作为依赖数组传递给useEffect,效果将仅在count状态更改时运行。
归档时间: |
|
查看次数: |
22375 次 |
最近记录: |