Reactstrap: UncontrolledDropdown in 8.0.0 does not find toggle function in this.context

Created on 8 Apr 2019  路  21Comments  路  Source: reactstrap/reactstrap

  • components: UncontrolledDrownDown
  • reactstrap version #8.0.0
  • import method umd/csj/es
  • react version #16.4.1
  • bootstrap version #4.3.1

What is happening?

On clicking the dropdown button in the UncontrolledDropdown component, an unhandled exception is raised and the dropdown menu does not appear.

What should be happening?

On clicking the dropdown button, the dropdown menu should appear.

Steps to reproduce issue

  1. Render the UncontrolledDropdown as provided in the docs
  2. Click on the button
  3. Observe that the dropdown menu does not toggle open and that there is an exception in the consolee.

Error message in console

Uncaught TypeError: this.context.toggle is not a function
    at ProxyComponent.onClick (DropdownToggle.js:59)
    at ProxyComponent.onClick (react-hot-loader.development.js:693)
    at ProxyComponent.onClick (Button.js:52)
    at ProxyComponent.onClick (react-hot-loader.development.js:693)
    at HTMLUnknownElement.callCallback (react-dom.development.js:100)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:138)
    at Object.invokeGuardedCallback (react-dom.development.js:187)
    at Object.invokeGuardedCallbackAndCatchFirstError (react-dom.development.js:201)
    at executeDispatch (react-dom.development.js:461)
    at executeDispatchesInOrder (react-dom.development.js:483)
    at executeDispatchesAndRelease (react-dom.development.js:581)
    at executeDispatchesAndReleaseTopLevel (react-dom.development.js:592)
    at forEachAccumulated (react-dom.development.js:562)
    at runEventsInBatch (react-dom.development.js:723)
    at runExtractedEventsInBatch (react-dom.development.js:732)
    at handleTopLevel (react-dom.development.js:4477)

Code

<UncontrolledDropdown>
                <DropdownToggle caret>
                    Dropdown
                </DropdownToggle>
                <DropdownMenu>
                    <DropdownItem header>Header</DropdownItem>
                    <DropdownItem disabled>Action</DropdownItem>
                    <DropdownItem>Another Action</DropdownItem>
                    <DropdownItem divider />
                    <DropdownItem>Another Action</DropdownItem>
                </DropdownMenu>
            </UncontrolledDropdown>
Hacktoberfest bug

Most helpful comment

Thanks to @leighmetzroth

I got a fix. I am also using [email protected] with [email protected] and I was also importing DropdownToggle from reactstrap directly. Adding UncontrolledDropdown as per example given by @leighmetzroth helped me out.

Earlier:

<DropdownToggle nav>
  <img src={avatar} className="img-avatar" alt="admin" />
</DropdownToggle>

Fixed code:

<UncontrolledDropdown>
  <DropdownToggle nav>
    <img src={avatar} className="img-avatar" alt="admin" />
  </DropdownToggle>
</UncontrolledDropdown>

All 21 comments

I've also tested the same example in reactstrap==7.1.0 and get the expected output.

This works with the latest version of react: https://stackblitz.com/edit/reactstrap-v8-6oafs2?file=Example.js

I can confirm that I am gettin the same error when using [email protected]. We'll have to look into it.

For now, the suggested workaround is to upgrade react to 16.8 if you can.

@twheys Hi, Did you solve the problem? I use [email protected] and [email protected], but i see this.context.toggle error :(

I also get this error with [email protected] and [email protected].

It turns about the minimum supported version for the way context is being consumed within DropdownToggle is react 16.6 which would explain why it works in newer version of react and not the 16.4 version reported here.
I know the minimum version of react listed in peerDeps for this project is 16.3, but we will need to bump that (Or change the way we consume the context.). I would prefer to change the way the context is consumed to support a more broad range of react versions (if anyone want to submit a PR).

For everyone reporting this issues while using the latest version of react, please provide an example using stackblitz or something similar as I cannot reproduce it in the latest version of react. (see my sample: stackblitz.com/edit/reactstrap-v8-6oafs2?file=Example.js) Chances are that you are not using the version of react you think you are using.

Using your example, I have worked out how to replicate it. It appears to only be an issue if you're importing the DropDownToggle (or DropDownMenu) directly out of the lib folder.

Here is the modified stackblitz with the issue:
https://stackblitz.com/edit/reactstrap-v8-j1pprd?file=Example.js

I am running [email protected] and also had the issue with [email protected] (there is no way I could be running an earlier version of react as I'm using hooks quite extensively).

So the simple "fix" is just to change how you do the imports for reactstrap.

Thanks to @leighmetzroth

I got a fix. I am also using [email protected] with [email protected] and I was also importing DropdownToggle from reactstrap directly. Adding UncontrolledDropdown as per example given by @leighmetzroth helped me out.

Earlier:

<DropdownToggle nav>
  <img src={avatar} className="img-avatar" alt="admin" />
</DropdownToggle>

Fixed code:

<UncontrolledDropdown>
  <DropdownToggle nav>
    <img src={avatar} className="img-avatar" alt="admin" />
  </DropdownToggle>
</UncontrolledDropdown>

Ok, so when importing directly it seems that two different versions/instances of DropdownContext are being used. To avoid this, always import things the same way (or know which import style is getting used under the hood.)
reactstrap provides a few different flavors to import: es, cjs, UMD, as src (as well as bundled and minified versions of each... because why not) (see here https://unpkg.com/[email protected]/ for the generated files)
Using different import types means that Dropdown from 'reactstrap' can end up getting /es/DropdownContext.js where DropdownToggle from 'reactstrap/lib/DropdownToggle' import references lib/DropdownContext.js These files are different and reference different context instances.

@TheSharpieOne with react 16.8 I get the same error. @YaroslavLyzlov no I did not solve the issue, but rolling back to reactstrap 7.1 fixed it for me.

I have the same Issue for me, version 8.0.0 React 16.x.x

@twheys, @canotech: Can you create an example showing the issue via something like stackblitz

@TheSharpieOne

import { PureComponent } from 'react'
import Link from 'next/link'
import Router from 'next/router'
import { connect } from 'react-redux'
import { FaUserCircle } from 'react-icons/fa'

import {
  NavLink,
  Collapse,
  Navbar,
  NavbarToggler,
  NavbarBrand,
  NavItem,
  Nav,
  UncontrolledDropdown,
  DropdownToggle,
  DropdownMenu,
  DropdownItem,
  Label,
} from 'reactstrap'
import { GoSignOut } from 'react-icons/go'

import { Routes } from '../../constants'
import { doLogOut} from '../../redux/modules/security'
import { logOut  } from '../../services/security'
import { openAlert } from '../../redux/modules/notificationsAlerts'


class NavbarComponent extends PureComponent {
  constructor(props){
    super(props)
    this.state = {
      isOpen: false,
    }
    this.logOutUser = this.logOutUser.bind(this)
    this.toggle = this.toggle.bind(this)
  }


  getRoutesWithoutAuthentication(){
    return this.props.url === Routes.assignPassword ||  this.props.url=== Routes.recoveryPassword || Routes.terms || Routes.privacy
  }

  logOutUser(){
    logOut().then((resp)=>{
      if(resp.status === 200){
        this.props.doLogOut()
        this.props.openAlert('success', '隆Adi贸s, Vuelve Pronto!')
        Router.push({pathname: Routes.home})
      }
    }).catch((e)=>{
      console.log('error',e)
      if(e){
        if(e.response){
          if(e.response.status !== 401)
            this.props.openAlert('error', 'Sucedi贸 un error al cerrar su sesi贸n. Intente de nuevo')
        }
      }
    })
  }

  toggle() {
    this.setState({
      isOpen: !this.state.isOpen
    })
  }

  render() {
    let unauthenticated = false
    if(!this.props.authenticated && this.getRoutesWithoutAuthentication()){
      unauthenticated = true
    }

    if(!this.props.authenticated && !unauthenticated) return null

    return (
      <Navbar color="primary" light expand="md" className="box-shadow" >
        <NavbarBrand href="/">
          <Link href={Routes.home}>
            <a>
              <img width="150px" src="/static/images/navbar-logo.png" alt="Dentegra B2B" />
            </a>
          </Link>
        </NavbarBrand>
        <NavbarToggler onClick={this.toggle}/>
        { !unauthenticated ?
          <Collapse isOpen={this.state.isOpen} navbar>
            <Nav className="ml-auto" navbar>
              <NavItem>
                <Link href={Routes.quotations}>
                  <NavLink href={Routes.quotations}>Cotizaciones</NavLink>
                </Link>
              </NavItem>
              <NavItem>
                <Link href={Routes.request}>
                  <NavLink  href={Routes.request}>Solicitudes</NavLink>
                </Link>
              </NavItem>
              <NavItem>
                <Link href={Routes.policies}>
                  <NavLink href={Routes.policies}>P贸lizas</NavLink>
                </Link>
              </NavItem>
              <NavItem>
                <Link href={Routes.notifications}>
                  <NavLink href={Routes.notifications}>Notificaciones</NavLink>
                </Link>
              </NavItem>
              <UncontrolledDropdown nav inNavbar>
                <DropdownToggle nav>
                  <FaUserCircle className="icon-size"/>
                </DropdownToggle>
                <DropdownMenu right>
                  <Link href={Routes.home}>
                    <a>
                      <DropdownItem>
                        <Label className="text-dark cursor-pointer"> Home </Label>
                      </DropdownItem>
                    </a>
                  </Link>
                  <Link href={Routes.profile}>
                    <a>
                      <DropdownItem>
                        <Label className="text-dark cursor-pointer"> Mi cuenta</Label>
                      </DropdownItem>
                    </a>
                  </Link>
                  <DropdownItem divider />
                  <DropdownItem onClick={()=>this.logOutUser()}>
                    Salir {' '} <GoSignOut />
                  </DropdownItem>
                </DropdownMenu>
              </UncontrolledDropdown>
            </Nav>
          </Collapse>: null
        }
      </Navbar>
    )
  }
}

export default connect(state => ({
  authenticated: state.security.authenticated,
  username: state.security.username,
}), { doLogOut, openAlert })(NavbarComponent)

@canotech the dropdown in your example works perfectly: https://stackblitz.com/edit/reactstrap-v8-n8zlcz?file=Example.js

Make sure the version of react you are using is 16.6+

https://stackblitz.com/edit/react-5evsqd

To reproduce the error, click on the dropdown button

@TheSharpieOne and also I make sure the version of react-dom is 16.8.+

That's works for me! thank you @TheSharpieOne !

Please expose the context as said in #1462 . There is no way to import it via /es/DropdownContext nor /lib/DropdownContext nor /src/DropdownContext in NextJs.

Every possibility returns the same error upon compilation Unexpected Identifier: (function (exports, require, module, __filename, __dirname) { import React from 'react';.

On standard Webpack + Babel configuration it works fine but still looks hacky.

This simply solved it for me. I added toggleprop with an empty function.

<Dropdown
        isOpen={isOpen}
        toggle={() => {}}
      >

may also work with UncontrolledDropdown haven't tried it tho.

@canotech the dropdown in your example works perfectly: https://stackblitz.com/edit/reactstrap-v8-n8zlcz?file=Example.js

Make sure the version of react you are using is 16.6+

I didn't even have to use UncontrolledDropdown to replace Dropdown. From the quoted example I found missing property "inNavbar" and added to Dropdown as below:

<Dropdown nav inNavbar isOpen={this.state.menuOpen} toggle={this.toggleMenu}>

The one property.

Using React 16.8.6 and Reactstrap 8.0.1

Anyone working on it. I would like to take this issue.

@onlywicked, It's all your. Thank you!

stackblitz.com/edit/react-5evsqd

To reproduce the error, click on the dropdown button

Dropdown requires onToggle, it is not provided in your example. If you used UncontrolledDropdown or provide onToggle with the minimum version of react mentioned above (16.6) it works fine.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

thecodejack picture thecodejack  路  22Comments

Rootmafia picture Rootmafia  路  17Comments

albertkim picture albertkim  路  18Comments

madisvain picture madisvain  路  17Comments

AlJohri picture AlJohri  路  16Comments