리액트 훅: 콜백 내에서 최신 상태로 접근
편집(2020년 6월 22일): 이 질문이 새로운 관심을 가지고 있기 때문에, 몇 가지 혼란스러운 점이 있을 수 있습니다.그래서 제가 강조하고 싶은 것은 질문의 예는 장난감의 예시를 의도한 것입니다.을 사용하다이 문제를 일으킨 문제는 콜백을 함수의 인수로 삼는 서드파티 라이브러리 사용(제한된 제어가 있음)에 있습니다.콜백에 최신 상태를 제공하는 올바른 방법은 무엇입니까?리액트 수업에서, 이것은 다음을 사용하여 수행될 것이다.this
있기 에 hooks,,, 음음음 of음 of 의 React.useState()
콜백이 스테이트를 취득했을 경우React.useState()
오래된 값(콜백이 셋업되었을 때의 값)이 됩니다.그러나 상태를 설정하면 전달된 인수를 통해 최신 상태에 액세스할 수 있습니다.즉, 상태를 이전과 동일하게 설정함으로써 리액트훅을 사용한 콜백의 최신 상태를 얻을 수 있습니다.이것은 효과가 있지만, 직관에 반한다.
--원래의 질문은 다음과 같습니다--
리액트 훅을 사용하여 콜백 내에서 상태를 읽으려고 합니다.콜백이 액세스 할 때마다 디폴트값으로 돌아옵니다.
음음음음은 계속 됩니다.Count is: 0
몇 번을 클릭하든 상관없습니다.
function Card(title) {
const [count, setCount] = React.useState(0)
const [callbackSetup, setCallbackSetup] = React.useState(false)
function setupConsoleCallback(callback) {
console.log("Setting up callback")
setInterval(callback, 3000)
}
function clickHandler() {
setCount(count+1);
if (!callbackSetup) {
setupConsoleCallback(() => {console.log(`Count is: ${count}`)})
setCallbackSetup(true)
}
}
return (<div>
Active count {count} <br/>
<button onClick={clickHandler}>Increment</button>
</div>);
}
const el = document.querySelector("#root");
ReactDOM.render(<Card title='Example Component' />, el);
이 코드는 여기서 찾을 수 있습니다.
콜백 내에서 상태를 설정하는 것은 문제가 없지만, 최신 상태에 액세스 할 수 있습니다.
추측컨대, 상태가 바뀌면 카드 기능의 새로운 인스턴스가 생성된다고 생각합니다.그리고 콜백이 예전 콜백을 가리키고 있다는 것도요.https://reactjs.org/docs/hooks-reference.html#functional-updates,의 문서를 바탕으로 콜백에서 setState를 호출하고 함수를 setState로 전달하여 setState 내에서 현재 상태에 액세스할 수 있는지 여부를 확인하는 방법을 생각해 보았습니다.교환
setupConsoleCallback(() => {console.log(`Count is: ${count}`)})
와 함께
setupConsoleCallback(() => {setCount(prevCount => {console.log(`Count is: ${prevCount}`); return prevCount})})
이 코드는 여기서 찾을 수 있습니다.
그 접근법 또한 효과가 없었다.편집: 실제로 두 번째 접근법은 효과가 있습니다.방금 콜백에 오타가 났어요.이것이 올바른 접근법입니다.이전 상태에 액세스하려면 setState를 호출해야 합니다.내가 국가를 세울 생각은 없지만.
리액트 수업도 비슷한 방식을 취했던 것 같은데.코드 일관성을 위해 리액트 이펙트를 계속 사용해야 합니다.
콜백 내에서 최신 상태 정보에 액세스하려면 어떻게 해야 합니까?
콜백을 에 전달할 수 없는 에는, 「」( 「」( 「」)를 사용할 수 .useRef
변경 가능한 오브젝트를 현재 상태로 유지합니다.다음과 같이 합니다.
function Card(title) {
const [count, setCount] = React.useState(0)
const [callbackSetup, setCallbackSetup] = React.useState(false)
const stateRef = useRef();
// make stateRef always have the current count
// your "fixed" callbacks can refer to this object whenever
// they need the current value. Note: the callbacks will not
// be reactive - they will not re-run the instant state changes,
// but they *will* see the current value whenever they do run
stateRef.current = count;
function setupConsoleCallback(callback) {
console.log("Setting up callback")
setInterval(callback, 3000)
}
function clickHandler() {
setCount(count+1);
if (!callbackSetup) {
setupConsoleCallback(() => {console.log(`Count is: ${stateRef.current}`)})
setCallbackSetup(true)
}
}
return (<div>
Active count {count} <br/>
<button onClick={clickHandler}>Increment</button>
</div>);
}
콜백은 현재 상태를 읽기 위해 가변 객체를 참조할 수 있습니다.종료 시 변경 가능한 객체를 캡처하고 모든 렌더 변경 가능한 객체가 현재 상태 값으로 업데이트됩니다.
2021년 6월 갱신:
NPM 을 합니다.react-usestateref
이치 React와 됩니다.useState
API.API.
사용 방법의 코드 예:
import useState from 'react-usestateref';
const [count, setCount, counterRef] = useState(0);
console.log(couterRef.current); // it will always have the latest state value
setCount(20);
console.log(counterRef.current);
NPM 패키지 react-useStateRef를 사용하면 최신 상태에 액세스할 수 있습니다(예:ref
를 합니다.useState
.
2020년 12월 갱신:
이 문제를 정확히 해결하기 위해 리액트 모듈을 만들었습니다. react-usestateref
(react useStateRef).§:
var [state, setState, ref] = useState(0);
useState
', '하다', '하다', '하다', '하다', '' 아래에 현재 를 알 수 있습니다.ref.current
상세:
원답
은 '마지막 값'을 수 있습니다.setState
예를 들어 다음과 같습니다.
var [state, setState] = useState(defaultValue);
useEffect(() => {
var updatedState;
setState(currentState => { // Do not change the state by getting the updated state
updateState = currentState;
return currentState;
})
alert(updateState); // the current state.
})
는 당신의했습니다. - a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a.setInterval
, 「」를 하고 있습니다.props
★★★★★★★★★★★★★★★★★」state
반응합니다.
조금 다른 방향에서 문제에 임함으로써 이미 좋은 답변을 추가할 수 있기를 바랍니다.리액트 문제가 아니라 오래된 Javascript 문제라는 것을 깨달았습니다.
여기서 주목받는 것은 React 훅 모델에 대한 생각이라고 생각합니다.이 모델에서는 상태 변수는 결국 로컬 변수일 뿐이므로 React 컴포넌트의 컨텍스트 내에서 스테이트풀한 것처럼 취급할 수 있습니다.실행 시 변수의 값은 항상 특정 상태의 후드 아래에 React가 유지되는 값이 됩니다.
React 함수의 변수를 합니다.setInterval
예를 들어, 추상화가 깨지면 상태 변수가 실제로는 값을 보유하는 로컬 변수일 뿐이라는 사실로 돌아갑니다.
추상화를 사용하면 실행 시 값이 항상 상태를 반영하는 것처럼 코드를 작성할 수 있습니다.React의 경우, 이는 사용자가 상태를 설정할 때마다 전체 함수가 다시 실행되고 변수의 값이 업데이트된 상태 값에 관계없이 React에 의해 설정되기 때문입니다.단, 콜백 내부에서는 이러한 일이 발생하지 않습니다.콜 시 기본 React 상태 값을 반영하도록 변수가 마법처럼 갱신되지 않습니다.콜백이 정의되었을 때 그대로입니다(이 경우).0
변경되지 않습니다.
여기서 해결책을 찾을 수 있습니다.이 로컬 변수가 가리키는 값이 실제로는 가변 객체에 대한 참조라면 상황은 달라집니다.이 값(기준)은 스택에서 일정하게 유지되지만 힙에서 참조되는 가변 값은 변경할 수 있습니다.
따라서 승인된 답변의 기법이 작동합니다. 즉, React ref는 가변 객체에 대한 정확한 참조를 제공합니다.하지만 나는 이것의 '반응' 부분이 단지 우연일 뿐이라는 것을 강조하는 것이 매우 중요하다고 생각한다.이 문제와 마찬가지로 이 솔루션은 React 자체와는 무관하며 React ref가 가변 객체에 대한 참조를 얻을 수 있는 한 가지 방법일 뿐입니다.
예를 들어 일반 Javascript 클래스를 사용하여 참조를 React 상태로 유지할 수도 있습니다.분명히 말씀드리면, 저는 이것이 더 나은 솔루션이라고 제안하는 것이 아니라, 단지 이 솔루션에 '반응' 측면이 없다는 점을 설명하기 위해 이 솔루션을 사용하는 것입니다.그냥 Javascript일 뿐입니다.
class Count {
constructor (val) { this.val = val }
get () { return this.val }
update (val) {
this.val += val
return this
}
}
function Card(title) {
const [count, setCount] = React.useState(new Count(0))
const [callbackSetup, setCallbackSetup] = React.useState(false)
function setupConsoleCallback(callback) {
console.log("Setting up callback")
setInterval(callback, 3000)
}
function clickHandler() {
setCount(count.update(1));
if (!callbackSetup) {
setupConsoleCallback(() => {console.log(`Count is: ${count.get()}`)})
setCallbackSetup(true)
}
}
return (
<div>
Active count {count.get()} <br/>
<button onClick={clickHandler}>Increment</button>
</div>
)
}
const el = document.querySelector("#root");
ReactDOM.render(<Card title='Example Component' />, el);
서 알 수 만으로 변경되지 수 있다는 입니다.setInterval
리액트 리액트 리액트 리액트 리액트 리액트.
다시 말하지만, 이것은 관용적인 Respect가 아니라 레퍼런스가 여기서 궁극적인 문제라는 점에 대한 요점을 보여줍니다.도움이 됐으면 좋겠네요!
할 수 .state
setState
하지만 하지 않아서 절대 하고 싶지 않아요.setState
이 경우, 다른 사람들이 당신의 코드를 읽을 때 혼란스러울 수 있습니다. 여러분이 을 더 잘 할 수 것 요.
function useExtendedState<T>(initialState: T) {
const [state, setState] = React.useState<T>(initialState);
const getLatestState = () => {
return new Promise<T>((resolve, reject) => {
setState((s) => {
resolve(s);
return s;
});
});
};
return [state, setState, getLatestState] as const;
}
사용.
const [counter, setCounter, getCounter] = useExtendedState(0);
...
getCounter().then((counter) => /* ... */)
// you can also use await in async callback
const counter = await getCounter();
라이브 데모
내의 , 「」를 해 .useEffect
에서 setState
이치노 되어 갱신됩니다.
대로 하면 이 될 것 요.useEffect()
setState
(클래스 기반 컴포넌트에서)의 두 번째 파라미터입니다.
는, 「」를 합니다.useEffect()
상태가 바뀌면 다음과 같이 영향을 받습니다.
const {
useState,
useEffect
} = React;
function App() {
const [count, setCount] = useState(0);
const decrement = () => setCount(count-1);
const increment = () => setCount(count+1);
useEffect(() => {
console.log("useEffect", count);
}, [count]);
console.log("render", count);
return (
<div className="App">
<p>{count}</p>
<button onClick={decrement}>-</button>
<button onClick={increment}>+</button>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render( < App / > , rootElement);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.6/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js"></script>
<div id="root"></div>
갱신하다
수 요.setInterval
렇게게불불불불불
const {
useState,
useEffect,
useRef
} = React;
function useInterval(callback, delay) {
const savedCallback = useRef();
// Remember the latest callback.
useEffect(() => {
savedCallback.current = callback;
}, [callback]);
// Set up the interval.
useEffect(() => {
function tick() {
savedCallback.current();
}
if (delay !== null) {
let id = setInterval(tick, delay);
return () => clearInterval(id);
}
}, [delay]);
}
function Card(title) {
const [count, setCount] = useState(0);
const callbackFunction = () => {
console.log(count);
};
useInterval(callbackFunction, 3000);
useEffect(()=>{
console.log('Count has been updated!');
}, [count]);
return (<div>
Active count {count} <br/>
<button onClick={()=>setCount(count+1)}>Increment</button>
</div>);
}
const el = document.querySelector("#root");
ReactDOM.render(<Card title='Example Component'/>, el);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.6/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js"></script>
<div id="root"></div>
제가 알고 있는 유일한 방법은 setState(current_value => ...) 함수를 호출하고 로직에서 current_value를 사용하는 것입니다.꼭 돌려주세요.예:
const myPollingFunction = () => {
setInterval(() => {
setState(latest_value => {
// do something with latest_value
return latest_value;
}
}, 1000);
};
는 @매우 @davnicwil의인 @davnicwil이 .useState
그게 무슨 뜻인지 더 명확할 수도 있어요
// once when the component is mounted
constructor(initialValue) {
this.args = Object.freeze([initialValue, this.updater]);
}
// every time the Component is invoked
update() {
return this.args
}
// every time the setState is invoked
updater(value) {
this.args = Object.freeze([value, this.updater]);
this.el.update()
}
'''의 ''initialValue
숫자 또는 문자열로 시작합니다(예: 1).
const Component = () => {
const [state, setState] = useState(initialValue)
}
워크스루
- 했을 때
useState
,this.args
1]= [1, ] - 시
useState
,this.args
없음 setState
2로 됩니다.this.args
2]= [2, ]- 에
useState
,this.args
없음
이 경우 특히 값을 지연하여 사용하는 작업을 수행합니다.
function doSomething(v) {
// I need to wait for 10 seconds
// within this period of time
// UI has refreshed multiple times
// I'm in step 4)
console.log(v)
}
// Invoke this function in step 1)
doSomething(value)
현재 복사본(당시)을 원래 복사본에 전달하기 때문에 "이전" 값이 지정됩니다.일일 ~일도 although although although although 。this.args
이치노 오래된 복사본이 변경되는 것은 아닙니다.을 사용법★★★★★★★★★★★★★★★!!
요약
그걸 바꾸기 위해서
- 값을 전달하지 않고 사용합니다.
- '어울리다'를 사용하세요.
object
경우 useRef
latest
값- 또는 다른 후크를 디자인합니다.
상기의 어프로치에서는 모두 수정되지만(다른 답변에서는), 문제의 근본 원인은 오래된 값을 함수에 전달하고 미래의 값으로 실행할 것으로 예상하기 때문입니다.여기서부터 문제가 생겼다고 생각합니다.해결책만 보면 잘 알 수 없습니다.
저도 같은 문제가 있었습니다만, 제 경우는 훅이 달린 레덕스를 사용하고 있었습니다.또, 같은 컴포넌트에서는 변이 상태가 되지 않았습니다. 시 값을 이 있었습니다.score
(서양하다그래서 그것에 대한 나의 생각은 꽤 간단하다.예전처럼 우아하지는 않지만 제 경우엔 효과가 있어서 누군가에게 도움이 될 수 있다는 희망을 가지고 이 자리에 올려놓았습니다.
const score = useSelector((state) => state.score);
const scoreRef = useRef(score);
useEffect(() => {
scoreRef.current = score ;
}, [score])
일반적인 생각은 최신 상태를 참조에 저장하는 것입니다. : )
이 두 가지를 조합해서 사용하겠습니다.setInterval()
★★★★★★★★★★★★★★★★★」useEffect()
.
setInterval()
컴포넌트를 마운트 해제한 후에 팝업이 발생할 수 있기 때문에 그 자체로는 문제가 있습니다.장난감 예에서는 이것이 문제가 되지 않지만 실제 환경에서는 콜백이 컴포넌트 상태를 변형시키고 싶어할 가능성이 높기 때문에 문제가 될 수 있습니다.useEffect()
그 자체로는 어떤 일이 일어나게 할 수 없습니다.useRef()
React의 기능 모델을 망가뜨릴 필요가 있는 드문 경우입니다.입력이나 기타 기능에 초점을 맞추는 등의 작업을 해야 하기 때문입니다.이치노
당신의 예는 별로 도움이 되지 않고 있으며, 타이머 팝이 얼마나 규칙적인지에 관심이 있는지 잘 모르겠습니다.따라서 이 기술을 사용하여 원하는 것을 대략적으로 달성하는 가장 간단한 방법은 다음과 같습니다.
import React from 'react';
const INTERVAL_FOR_TIMER_MS = 3000;
export function Card({ title }) {
const [count, setCount] = React.useState(0)
React.useEffect(
() => {
const intervalId = setInterval(
() => console.log(`Count is ${count}`),
INTERVAL_FOR_TIMER_MS,
);
return () => clearInterval(intervalId);
},
// you only want to restart the interval when count changes
[count],
);
function clickHandler() {
// I would also get in the habit of setting this way, which is safe
// if the increment is called multiple times in the same callback
setCount(num => num + 1);
}
return (
<div>
Active count {count} <br/>
<button onClick={clickHandler}>Increment</button>
</div>
);
}
주의: 타이머가 팝업되면1초 후에 클릭하면 다음 로그가 이전 로그보다4초 뒤에 표시됩니다.클릭하면 타이머가 리셋되기 때문입니다.
하려면 , 「이렇게 하다」, 「이렇게 하다」를 사용하는 방법이 될 것입니다.Date.now()
useState()
하여 사용할 수 있습니다.setTimeout()
setInterval()
.
다음 타이머 팝을 저장해야 하기 때문에 조금 더 복잡하지만 나쁘지 않습니다.또, 그 복잡성은, 새로운 기능을 사용하는 것만으로 추상화할 수 있습니다.요약하자면, 후크를 사용하여 정기 타이머를 시작하는 안전한 "Reacty" 방법이 있습니다.
import React from 'react';
const INTERVAL_FOR_TIMER_MS = 3000;
const useInterval = (func, period, deps) => {
const [nextPopTime, setNextPopTime] = React.useState(
Date.now() + period,
);
React.useEffect(() => {
const timerId = setTimeout(
() => {
func();
// setting nextPopTime will cause us to run the
// useEffect again and reschedule the timeout
setNextPopTime(popTime => popTime + period);
},
Math.max(nextPopTime - Date.now(), 0),
);
return () => clearTimeout(timerId);
}, [nextPopTime, ...deps]);
};
export function Card({ title }) {
const [count, setCount] = React.useState(0);
useInterval(
() => console.log(`Count is ${count}`),
INTERVAL_FOR_TIMER_MS,
[count],
);
return (
<div>
Active count {count} <br/>
<button onClick={() => setCount(num => num + 1)}>
Increment
</button>
</div>
);
}
를 모두 .deps
와)useEffect()
인터벌 상태 등할 수 상태가 확신할 수 있습니다. , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , 등입니다.
언급URL : https://stackoverflow.com/questions/57847594/react-hooks-accessing-up-to-date-state-from-within-a-callback
'programing' 카테고리의 다른 글
"Router"에서는 "history"라는 프로포트가 "required"로 표시되어 있지만, 그 값은 "undefined"입니다. (0) | 2023.03.11 |
---|---|
왜 ng-href 지령에는 {{}}이(가) 필요한데 다른 지령에는 필요하지 않은가? (0) | 2023.03.11 |
jsonpath 식을 사용한 어레이 크기 - Stefan Goessner JsonPath (0) | 2023.03.06 |
스프링 데이터 - 값이 null인 경우 매개 변수 무시 (0) | 2023.03.06 |
사용자 지정 위젯이 Visual Composer에 표시되지 않음 (0) | 2023.03.06 |