1

I have a demo

It's a simple React app using Typescript.

I'm trying to use Reacts Context api

I've created a context ThemeContext with simple theme styling values to use in other components.

I'm then using this theme context to styled a list of books Booklist.tsx

I want a button to update the theme

I have a themetoggle in the context that I'm trying to call from the ThemeToggle component.

My problem is the onClick in this ThemeToggle I get the error

(JSX attribute) React.DOMAttributes<HTMLButtonElement>.onClick?: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void

Type 'boolean' is not assignable to type '(event: MouseEvent<HTMLButtonElement, MouseEvent>) => void'.(2322)

ThemeToggle.tsx(23, 29): The expected type comes from property 'onClick' which is declared here on type 'DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>'

I'm sure this is a typescript error but not sure how to fix it.

Whats casuing this error on the onClick, how can I fix it

index.tsx

import React from 'react';
import BookList from './Booklist';
import ThemeContextProvider from './ThemeContext';
import { render } from "react-dom";
import ThemeToggle from './ThemeToggle';
import './style.css'

const App:React.FC = () => {
  return (
    <div className="App">
      <ThemeContextProvider>
        <BookList />
        <ThemeToggle/>
      </ThemeContextProvider>
    </div>
  );
}

export default App;
render(<App />, document.getElementById("root"));

ThemeContext.tsx

import React, {createContext} from 'react'

export interface Props {}

export interface State {
    lightTheme: boolean
    light:{syntax: string, ui: string, bg: string}
    dark:{syntax: string, ui: string, bg: string}
    toggleTheme:boolean
}

const defaultState:State = {
    lightTheme: true,
    light:{syntax:'', ui:'', bg:''},
    dark:{syntax:'', ui:'', bg:''},
    toggleTheme:false
}

export const ThemeContext = createContext(defaultState)

class ThemeContextProvider extends React.Component<Props, State> {

    constructor(props: Props){
        super(props)

        this.state = { 
            lightTheme: true,
            light: {syntax: '#333', ui: 'pink', bg: '#bbb'},
            dark: {syntax: '#ddd', ui: '#333', bg: '#555'},
            toggleTheme: false
        }
    }

    toggleTheme = () => {
        this.setState({ lightTheme: !this.state.lightTheme})
    }

    render() { 
        return (  
            <ThemeContext.Provider value={{...this.state, toggleTheme: this.state.toggleTheme}}>
                {this.props.children}
            </ThemeContext.Provider>
        );
    }
}

export default ThemeContextProvider;

Booklist.tsx

import React from 'react'
import { ThemeContext } from './ThemeContext';

export interface Props {}

export interface State {
    lightTheme: boolean
    light:Darkness
    dark:Darkness
}

interface Darkness{
    syntax: string
    ui: string
    bg: string 
}

class BookList extends React.Component<Props, State> {
    render() { 
      return(
        <ThemeContext.Consumer>
        {(state) => {
          const { lightTheme, light, dark} = state
          const theme = lightTheme ? light : dark
          return(
                  <div style={{color: theme.syntax, background: theme.bg}}>
                      <ul>
                          <li style={{background:theme.ui}}>Book One</li>
                          <li style={{background:theme.ui}}>Book Two</li>
                      </ul>
                  </div>
          )
        }}</ThemeContext.Consumer>
      )
    }
}

export default BookList;        

ThemeToggle

import * as React from 'react';
import { ThemeContext } from './ThemeContext';

export interface ThemeToggleProps {}

export interface ThemeToggleState {}

class ThemeToggle  extends React.Component<ThemeToggleProps, ThemeToggleState> {
    render() { 
        return ( 
            <ThemeContext.Consumer>
                {(state) => {
                    const {toggleTheme} = state
                    return(
                        <button onClick={toggleTheme}>Toggle Theme</button>
                    )
                }}
            </ThemeContext.Consumer>
         );
    }
}

export default ThemeToggle ;    
4
  • toggleTheme is a boolean, not a function. You have to make it a function if you want to bind it to onClick. Commented Sep 21, 2020 at 13:06
  • Agreed with @PhilipFeldmann. Also, you should provide more code - think about the parts of your code people will want to see to understand your problem. What is the code for onClick ? What is the code where you bind this handler to your JSX? Commented Sep 21, 2020 at 13:07
  • @mbdavis: That code is in the demo OP linked in the first line. Commented Sep 21, 2020 at 13:21
  • I have updated the question now with the code - toggleTheme is a function in ThemeContext Commented Sep 21, 2020 at 13:23

1 Answer 1

2

Like @PhilipFeldmann wrote in their comment, the onclick attribute expects a function, not a boolean.

You declare the interface State in ThemeContext.tsx:12, where the property toggleTheme is declared as boolean. You keep that up, assigning the property in the const defaultState a boolean value, same when you assign ThemeContextProvider.state with an object whose toggleTheme property is a boolean. Then you pass that down to the ThemeToggle class, where toggleTheme is still a boolean (ThemeToggle.tsx:13).

You can fix that by changing the declaration of the State interface so that toggleTheme takes a function instead:

// ThemeContext.tsx:5
export interface State {
    lightTheme: boolean
    light:{syntax: string, ui: string, bg: string}
    dark:{syntax: string, ui: string, bg: string}
    toggleTheme:() => void
}
// ThemeContext.tsx:12
const defaultState:State = {
    lightTheme: true,
    light:{syntax:'', ui:'', bg:''},
    dark:{syntax:'', ui:'', bg:''},
    toggleTheme: () => {}
}
// ThemeContext.tsx:26
this.state = { 
    lightTheme: true,
    light: {syntax: '#333', ui: 'pink', bg: '#bbb'},
    dark: {syntax: '#ddd', ui: '#333', bg: '#555'},
    toggleTheme: this.toggleTheme
}

With that the button will work.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.