Could anyone advise me on the best way to convert this hook to a type safe version using Typescript please. Its a simple toggle to display a different 'thing' on toggle state.
useToggle.js
const useToggleButton = ({on,off}) => {
const [toggle, setToggle] = React.useState(false)
const ToggleButton = () => (
<div
role="button"
onClick={() => setToggle(!toggle)}
data-testid="portal-toggle-btn"
>
{toggle? on : off}
</div>
)
return [ToggleButton, toggle, setToggle]
}
The thing is that it returns an array with the component, state and setState function. My attempt is below but I get the errors
TS2605: JSX element type 'IReturnType' is not a constructor function for JSX elements. Type 'IReturnType' is missing the
following properties from type 'Element': type, props, key
TS2739: Type '(boolean | Dispatch<SetStateAction<boolean>>)[]' is
missing the following properties from type 'IReturnType': component,
state, func
useToggle.tsx
import * as React from 'react'
interface IToggleBntProps {
on: any
off: any
}
interface IState {
bol: boolean
}
interface IReturnType {
component: React.FunctionComponent
state: boolean
func: (bol: boolean) => IState
}
const useToggleButton = ({ on, off }: IToggleBntProps): IReturnType => {
const [toggle, setToggle] = React.useState(false)
const ToggleButton = () => (
<div
role="button"
onClick={() => setToggle(!toggle)}
data-testid="portal-toggle-btn"
>
{toggle ? on : off}
</div>
)
return [ToggleButton, toggle, setToggle]
}
export default useToggleButton
Benefits of TypeScript with Hooks Local state types are often inferred by the default state values. Components with hooks are all functions. So, we can write the same component returning the FC<P> type defined in the React library. The function explicitly declares its return type, setting along the props type.
useEffect. useEffect is here for all side effects. Adding event listeners, changing things in the document, fetching data. Everything you would use component lifecycle methods for ( componentDidUpdate , componentDidMount , componentWillUnmount ) The method signature is pretty straightforward.
Don't call Hooks inside loops, conditions, or nested functions. Instead, always use Hooks at the top level of your React function, before any early returns. By following this rule, you ensure that Hooks are called in the same order each time a component renders.
Hooks are JavaScript functions that manage the state's behaviour and side effects by isolating them from a component. So, we can now isolate all the stateful logic in hooks and use (compose them, as hooks are functions, too) into the components.
TypeScript can actually figure out most of these types automatically!
In the code below, saying as const
(a const assertion) at the end of your return statement lets TypeScript correctly infer the return type you want: [() => JSX.Element, boolean, React.Dispatch<React.SetStateAction<boolean>>]
.
If you wanted to be explicit, you could do type IReturnType = [() => JSX.Element, boolean, React.Dispatch<React.SetStateAction<boolean>>]
, but that's verbose and unnecessary.
interface IToggleBntProps {
on: any
off: any
}
const useToggleButton = ({ on, off }: IToggleBntProps) => {
const [toggle, setToggle] = React.useState(false)
const ToggleButton = () => (
<div
role="button"
onClick={() => setToggle(!toggle)}
data-testid="portal-toggle-btn"
>
{toggle ? on : off}
</div>
)
// saying 'as const' here means that TypeScript automatically
// knows that this should be a tuple type of three elements.
// Not you need TypeScript 3.4 or newer
return [ToggleButton, toggle, setToggle] as const;
}
export default useToggleButton
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With