我正在进度条上(最终是..),我想在达到某个值(10、100,...,N)时停止动画(调用cancelAnimationRequest
)并将其重置为0。
但是,使用我当前的代码,它会重置为0,但会无限期地运行。我认为这部分代码可能有问题:
setCount((prevCount) => {
console.log('requestRef.current', requestRef.current, prevCount);
if (prevCount < 10) return prevCount + deltaTime * 0.001;
// Trying to cancel the animation here and reset to 0:
cancelAnimationFrame(requestRef.current);
return 0;
});
这是整个示例:
const Counter = () => {
const [count, setCount] = React.useState(0);
// Use useRef for mutable variables that we want to persist
// without triggering a re-render on their change:
const requestRef = React.useRef();
const previousTimeRef = React.useRef();
const animate = (time) => {
if (previousTimeRef.current != undefined) {
const deltaTime = time - previousTimeRef.current;
// Pass on a function to the setter of the state
// to make sure we always have the latest state:
setCount((prevCount) => {
console.log('requestRef.current', requestRef.current, prevCount);
if (prevCount < 10) return prevCount + deltaTime * 0.001;
// Trying to cancel the animation here and reset to 0:
cancelAnimationFrame(requestRef.current);
return 0;
});
}
previousTimeRef.current = time;
requestRef.current = requestAnimationFrame(animate);
}
React.useEffect(() => {
requestRef.current = requestAnimationFrame(animate);
return () => cancelAnimationFrame(requestRef.current);
}, []);
return <div>{ Math.round(count) }</div>;
}
ReactDOM.render(<Counter />, document.getElementById('app'));
html {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
}
body {
font-size: 60px;
font-weight: 700;
font-family: 'Roboto Mono', monospace;
color: #5D9199;
background-color: #A3E3ED;
}
.as-console-wrapper {
max-height: 66px !important;
}
<script src="https://unpkg.com/react@16.7.0-alpha.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.development.js"></script>
<div id="app"></div>
答案 0 :(得分:1)
代码的主要问题是您试图取消已经执行的更新。相反,您可以避免请求不需要的最后一次更新。您可以在下面看到问题和简单的解决方案:
const Counter = () => {
const [count, setCount] = React.useState(0);
const requestRef = React.useRef();
const previousTimeRef = React.useRef(0);
const animate = React.useCallback((time) => {
console.log(' RUN:', requestRef.current);
setCount((prevCount) => {
const deltaTime = time - previousTimeRef.current;
const nextCount = prevCount + deltaTime * 0.001;
// We add 1 to the limit value to make sure the last valid value is
// also displayed for one whole "frame":
if (nextCount >= 11) {
console.log(' CANCEL:', requestRef.current, '(this won\'t work as inteneded)');
// This won't work:
// cancelAnimationFrame(requestRef.current);
// Instead, let's use this Ref to avoid calling `requestAnimationFrame` again:
requestRef.current = null;
}
return nextCount >= 11 ? 0 : nextCount;
});
// If we have already reached the limit value, don't call `requestAnimationFrame` again:
if (requestRef.current !== null) {
previousTimeRef.current = time;
requestRef.current = requestAnimationFrame(animate);
console.log('- SCHEDULE:', requestRef.current);
}
}, []);
React.useEffect(() => {
requestRef.current = requestAnimationFrame(animate);
return () => cancelAnimationFrame(requestRef.current);
}, []);
// This floors the value:
// See https://stackoverflow.com/questions/7487977/using-bitwise-or-0-to-floor-a-number.
return (<div>{ count | 0 } / 10</div>);
};
ReactDOM.render(<Counter />, document.getElementById('app'));
html {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
}
body {
font-size: 60px;
font-weight: 700;
font-family: 'Roboto Mono', monospace;
color: #5D9199;
background-color: #A3E3ED;
}
.as-console-wrapper {
max-height: 66px !important;
}
<script src="https://unpkg.com/react@16.7.0-alpha.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.development.js"></script>
<div id="app"></div>
在任何情况下,您更新状态的次数都比实际需要的次数多,这可以通过使用time
和requestAnimationFrame
提供的时间戳(requestAnimationFrame
)来避免跟踪当前和下一个/目标计数器值。您仍将多次调用setCount(...)
更新函数,但是只有在知道更改将反映在UI中后,才更新状态(const Counter = ({ max = 10, rate = 0.001, location }) => {
const limit = max + 1;
const [count, setCount] = React.useState(0);
const t0Ref = React.useRef(Date.now());
const requestRef = React.useRef();
const targetValueRef = React.useRef(1);
const animate = React.useCallback(() => {
// No need to keep track of the previous time, store initial time instead. Note we can't
// use the time param provided by requestAnimationFrame to the callback, as that one won't
// be reset when the `location` changes:
const time = Date.now() - t0Ref.current;
const nextValue = time * rate;
if (nextValue >= limit) {
console.log('Reset to 0');
setCount(0);
return;
}
const targetValue = targetValueRef.current;
if (nextValue >= targetValue) {
console.log(`Update ${ targetValue - 1 } -> ${ nextValue | 0 }`);
setCount(targetValue);
targetValueRef.current = targetValue + 1;
}
requestRef.current = requestAnimationFrame(animate);
}, []);
React.useEffect(() => {
requestRef.current = requestAnimationFrame(animate);
return () => cancelAnimationFrame(requestRef.current);
}, []);
React.useEffect(() => {
// Reset counter if `location` changes, but there's no need to call `cancelAnimationFrame` .
setCount(0);
t0Ref.current = Date.now();
targetValueRef.current = 1;
}, [location]);
return (<div className="counter">{ count } / { max }</div>);
};
const App = () => {
const [fakeLocation, setFakeLocation] = React.useState('/');
const handleButtonClicked = React.useCallback(() => {
setFakeLocation(`/${ Math.random().toString(36).slice(2) }`);
}, []);
return (<div>
<span className="location">Fake Location: { fakeLocation }</span>
<Counter max={ 10 } location={ fakeLocation } />
<button className="button" onClick={ handleButtonClicked }>Update Parent</button>
</div>);
};
ReactDOM.render(<App />, document.getElementById('app'));
)。>
html {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
}
body {
font-family: 'Roboto Mono', monospace;
color: #5D9199;
background-color: #A3E3ED;
}
.location {
font-size: 16px;
}
.counter {
font-size: 60px;
font-weight: 700;
}
.button {
border: 2px solid #5D9199;
padding: 8px;
margin: 0;
font-family: 'Roboto Mono', monospace;
color: #5D9199;
background: transparent;
outline: none;
}
.as-console-wrapper {
max-height: 66px !important;
}
<script src="https://unpkg.com/react@16.7.0-alpha.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.development.js"></script>
<div id="app"></div>
@Configuration
public class AppConfig {
@Bean
public DemoClass service()
{
}
}