Using a hook in react to change boolean and get a callback

Viewed 1450

I'm trying to use a hook in React. The hook should give me a boolean and a callback. When the callback gets called the boolean should change value. When I try to retrieve the value and the callback I get an error.

Property isEnabled does not exist on type (boolean | (() => void))[]. On the line const {isEnabled, toggleCallback} = useToggle();

What am I doing wrong?

function toggleCallback(toggleCallback: () => void) {
    toggleCallback();
}

export default function FakePage(props: any) {
    const {isEnabled, toggleCallback} = useToggle();
    return (
        <div>
            <ComponentOne isEnabled={isEnabled}/>
            <button onClick={() => testToggle(toggleCallback)}>Test</button>
        </div>
    );
}

// hook
export default function useToggle() {
    let isEnabled: boolean = true;
    const toggleCallback= useCallback(() => {
        isEnabled = !isEnabled;
    }, [isEnabled]);
    return [isEnabled, toggleCallback];
}
2 Answers

As for the error and types, currently you are returning a list which contains boolean types and functions of type () => void, where you want to return a tuple.

// Return from custom hook
return [isEnabled, toggleCallback];

// use it like so, and not as object { isEnabled,setIsEnbaled }
const [isEnabled,setIsEnbaled] = useToggle();

Fix the return types with:

return [isEnabled, toggleCallback] as const;

Or specify the return type:

type HookProps = (initialState: boolean) => [boolean, () => void];
const useToggle: HookProps = (initialState) => {...}

As for the hook implementation, it should look in JS like so, as variables are not connected to state, using toggleCallback in your example won't trigger a render.

function useToggle(initialValue = true) {
  const [isEnabled, toggle] = useReducer((p) => !p, true);
  return [isEnabled, toggle];
}

how to create custom hook :

export default function FakePage(props: any) {
    const {isEnabled, toggleCallback} = useToggle();
    return (
        <div>
            <ComponentOne isEnabled={isEnabled}/>
            <button onClick={toggleCallback}>Test</button>
        </div>
    );
}

// hook
export default function useToggle() {
    const [isEnabled,setIsEnabled] = useState(true)
    const toggleCallback= () => {
       setIsEnabled(current=>!current);
    };// no need to useCallback
    return {isEnabled, toggleCallback};
}

// and if you want to pass default value 

   export default function useToggle(default) {
        const [isEnabled,setIsEnabled] = useState(default)
        const toggleCallback= () => {
           setIsEnabled(current=>!current);
        };// no need to useCallback
        return {isEnabled, toggleCallback};
    }
// usage 
const {isEnabled, toggleCallback} = useToggle(false);
Related