I'm trying to rewrite the App
component in this CodePen as a Functional component using Typescript.
However, I am getting error like this when trying to run it:
ERROR in src/App.tsx:13:14
TS2339: Property 'forceUpdateHandler' does not exist on type 'MutableRefObject<Spinner | null>'.
11 |
12 | const handleClick = () => {
> 13 | _child1?.forceUpdateHandler();
| ^^^^^^^^^^^^^^^^^^
14 | _child2?.forceUpdateHandler();
15 | _child3?.forceUpdateHandler();
16 | };
What is the correct way to handle Spinner.forceUpdateHandler
?
Here's my attempt:
App.tsx
Rewriting the class component as a functional component This has been simplified from the original to focus on the problematic area
import React, { useRef } from "react";
import "./App.css";
import Spinner from "./Spinner.js";
const App = () => {
const [matches, setMatches] = React.useState<number[]>([]);
const _child1 = useRef<Spinner | null>(null);
const _child2 = useRef<Spinner | null>(null);
const _child3 = useRef<Spinner | null>(null);
const handleClick = () => {
_child1?.forceUpdateHandler();
_child2?.forceUpdateHandler();
_child3?.forceUpdateHandler();
};
const finishHandler = (value: number) => {
setMatches([...matches, value]);
if (matches.length === 3) {
console.log("Done");
emptyArray();
}
};
const emptyArray = () => {
setMatches([]);
};
return (
<div>
<div className={`spinner-container`}>
<Spinner onFinish={finishHandler} ref={_child1} timer="1000" />
<Spinner onFinish={finishHandler} ref={_child2} timer="1400" />
<Spinner onFinish={finishHandler} ref={_child3} timer="2200" />
<div className="gradient-fade"></div>
</div>
<button onClick={handleClick}>SPIN!!!</button>
</div>
);
};
export default App;
Spinner.js
Same as in the above CodePen, with imports and exports added
import React from "react";
class Spinner extends React.Component {
constructor(props) {
super(props);
this.forceUpdateHandler = this.forceUpdateHandler.bind(this);
}
forceUpdateHandler() {
this.reset();
}
reset() {
if (this.timer) {
clearInterval(this.timer);
}
this.start = this.setStartPosition();
this.setState({
position: this.start,
timeRemaining: this.props.timer,
});
this.timer = setInterval(() => {
this.tick();
}, 100);
}
state = {
position: 0,
lastPosition: null,
};
static iconHeight = 188;
multiplier = Math.floor(Math.random() * (4 - 1) 1);
start = this.setStartPosition();
speed = Spinner.iconHeight * this.multiplier;
setStartPosition() {
return Math.floor(Math.random() * 9) * Spinner.iconHeight * -1;
}
moveBackground() {
this.setState({
position: this.state.position - this.speed,
timeRemaining: this.state.timeRemaining - 100,
});
}
getSymbolFromPosition() {
let { position } = this.state;
const totalSymbols = 9;
const maxPosition = Spinner.iconHeight * (totalSymbols - 1) * -1;
let moved = (this.props.timer / 100) * this.multiplier;
let startPosition = this.start;
let currentPosition = startPosition;
for (let i = 0; i < moved; i ) {
currentPosition -= Spinner.iconHeight;
if (currentPosition < maxPosition) {
currentPosition = 0;
}
}
this.props.onFinish(currentPosition);
}
tick() {
if (this.state.timeRemaining <= 0) {
clearInterval(this.timer);
this.getSymbolFromPosition();
} else {
this.moveBackground();
}
}
componentDidMount() {
clearInterval(this.timer);
this.setState({
position: this.start,
timeRemaining: this.props.timer,
});
this.timer = setInterval(() => {
this.tick();
}, 100);
}
render() {
let { position, current } = this.state;
return (
<div
style={{ backgroundPosition: "0px " position "px" }}
className={`icons`}
/>
);
}
}
export default Spinner;
CodePudding user response:
just an idea may be you should try redefining the Spinner type in your functional component
type SpinnerProps ={
forceUpdateHandler: () => void,
startPosition: () => number,
.... // TODO all the other props need to be defined
}
const _child1 = useRef<SpinnerProps | null>(null);
CodePudding user response:
Ref
's hold the actual reference in the current
property, so it should actually be:
const handleClick = () => {
_child1?.current?.forceUpdateHandler();
_child2?.current?.forceUpdateHandler();
_child3?.current?.forceUpdateHandler();
};
You can read more about it here