Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Button component doesn't work with useContext hook

I am practicing useContext hook with Next.js if it matters and created a small site that changes background color of <Paragraph></Paragraph> element for printing text and <Button></Button> element to change theme. I organized it with <Layout /> where button and paragraph are placed.

As written in tutorial, I can place this theme change logic into onClick method and it will work. It actually works, but only if I put a simple <button></button> element, but not react component:

<ThemeContext.Provider value={theme}>
                  <Paragraph>Hello Context</Paragraph>
 // Doesn't work ---> <Button type="button" onClick={() => {theme === 'dark' ? setTheme('light') : setTheme('dark')}}>Change theme</Button>
 // Works        ---> <button type="button" onClick={() => {theme === 'dark' ? setTheme('light') : setTheme('dark')}}>Change theme</button>
            </ThemeContext.Provider>

Paragraph.tsx:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

import { useContext, useState, ReactNode, DetailedHTMLProps, HTMLAttributes } from 'react';
import styles from './Paragraph.module.scss'
import cn from 'classnames'
import { ThemeContext } from '../../Layout/Layout'

interface ParagraphProps extends DetailedHTMLProps<HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement> {  
      children: ReactNode
}

export const Paragraph = ({ children, className }: ParagraphProps): JSX.Element => {
      const theme = useContext<string>(ThemeContext)

      return (
            <p className={cn(styles.p, className, {
                  [styles.paraDark]: theme === 'dark',
                  [styles.paraLight]: theme === 'light'
            })}>
                  {children}
            </p>
      )
}

Button.tsx:

import { DetailedHTMLProps, ButtonHTMLAttributes, ReactNode, useContext, useState, MouseEventHandler } from 'react';
import { ThemeContext } from 'Layout/Layout';
import cn from 'classnames'
import styles from './Button.module.scss'

interface ButtonProps extends DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement> {
      children: ReactNode
}
 
export const Button = ({ children, className}: ButtonProps ): JSX.Element => {
      const theme = useContext<string>(ThemeContext)

      return (
            <button
                  className={cn(styles.button, className, {
                        [styles.buttonDark]: theme === 'dark',
                        [styles.buttonLight]: theme === 'light'
                  })}
            >
                  {children}
            </button>
      )
}

Layout.tsx:

import { Paragraph, Button } from "Components";
import { createContext, useState } from 'react';

interface Layout {}

export const ThemeContext = createContext<string>('')

export const Layout = (): JSX.Element => {
      const [theme, setTheme] = useState<string>('dark')

      // Putting this function into onClick event also doesn't help
      const handleChangeThemeButtonClick = () => {
            if (theme === 'dark')
                  setTheme('light')
            else if (theme === 'light')
                  setTheme('dark')
      }

      return (
            <ThemeContext.Provider value={theme}>
                  <Paragraph>Hello Context</Paragraph>
                  <Button type="button" onClick={() => {theme === 'dark' ? setTheme('light') : setTheme('dark')}}>Change theme</Button>
                  <button type="button" onClick={() => {theme === 'dark' ? setTheme('light') : setTheme('dark')}}>Change theme</button>
            </ThemeContext.Provider>
      )
}

I was learing this hook on react beta docs and my code looks identical to what’s written in this tutorial.

>Solution :

Your Button component doesn’t have an onClick property.

You need to pass it down as a prop and call it from the native button onClick property.

Since you’re using typescript, give it the appropriate type of MouseEventHandler<HTMLButtonElement>, and mark it as optional (with ? after the property name in the type declaration) in case you want to use Button without an onClick event.

import { DetailedHTMLProps, ButtonHTMLAttributes, ReactNode, useContext, useState, MouseEventHandler } from 'react';
import { ThemeContext } from 'Layout/Layout';
import cn from 'classnames'
import styles from './Button.module.scss'

interface ButtonProps extends DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement> {
      children: ReactNode
      onClick?: MouseEventHandler<HTMLButtonElement> // <= pass it down as a prop with the appropriate type
}
 
export const Button = ({ children, className, onClick }: ButtonProps ): JSX.Element => {
      const theme = useContext<string>(ThemeContext)

      return (
            <button
                  className={cn(styles.button, className, {
                        [styles.buttonDark]: theme === 'dark',
                        [styles.buttonLight]: theme === 'light'
                  })}
                  onClick={onClick}
            >
                  {children}
            </button>
      )
}
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading