React.js Context API: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components)
Asked Answered
O

1

6

Consider the following scenario:

import React, { Component } from 'react';
import LocaleService from '../Services/LocaleService.js';

const defaultStore = {
    loaded: false,
    locales: []
};

const LocalesContext = React.createContext(defaultStore);
class LocalesProvider extends Component
{
    state = defaultStore;

    load() {
        const service = new LocaleService(), that = this;
        service.fetch().then(function (locales) {
            that.setState({ locales: locales, loaded: true });
        });
    }

    data() {
        return this.state;
    }

    componentDidMount() {
        this.load();
    }

    render() {
        return (
            <LocalesContext.Provider value={this.data()}>
                {this.props.children}
            </LocalesContext.Provider>
        );
    }
}

export default LocalesProvider;
import React, { Component } from 'react';
import Sidebar from './Sidebar.js';
import Topbar from './Topbar.js';
import Content from './Content.js';
import LocalesProvider from './Providers/LocalesProvider.js';

class App extends Component
{
    state = {
        ready: true
    }

    render() {
        if (this.state.ready) {
            return (
                <div>
                    <Topbar/>
                    <section className="section">
                        <section className="columns" style={{height: '100vh'}}>
                            <div>
                                <LocalesProvider.Consumer>
                                    { data => 
                                        (
                                            <Sidebar isReady={data.loaded} locales={data.locales}/>
                                        )
                                    }
                                </LocalesProvider.Consumer>
                            </div>
                            <main className="column" style={{overflow: 'auto', position: 'relative'}}>
                                <Content/>
                            </main>
                        </section>
                    </section>
                </div>
            );
        } else {
            return ('Loading...');
        }
    }
}

export default App;
import React, { Component } from 'react';
import LocalesProvider from './Providers/LocalesProvider.js';
import { NavLink, HashRouter } from "react-router-dom";

class Sidebar extends Component
{
  constructor(props) {
    super(props);
  }

  buildLocaleLinks (locales, uri) {
    if (!this.props.isReady) {
      return 'Loading...';
    } 

    if (!locales.length) {
      return null;
    }

    return locales.map(function (locale) {
      return (
        <li key={'navigation.translate.' + locale.props.key}>
          <NavLink replace to={'/' + uri + '/' + locale.props.key}>
            {locale.props.key}
          </NavLink>
        </li>
      );
    })
  }

    render () {
        return (
    <HashRouter>
        <aside className="menu">
          <p className="menu-label">
            Menu
          </p>
            <ul className="menu-list">
            <li>Translate</li>
            <li>
              <ul>
                <LocalesProvider.Consumer>
                  {locales => (
                    this.buildLocaleLinks(locales, 'translate')
                  )}
                </LocalesProvider.Consumer>
              </ul>
            </li>
            <li>Validate</li>
            <li>
              <ul>
                <LocalesProvider.Consumer>
                  {locales => (
                    this.buildLocaleLinks(locales, 'validate')
                  )}
                </LocalesProvider.Consumer>
              </ul>
            </li>
          </ul>
        </aside>
      </HashRouter>
        );
    }
}

export default Sidebar;

Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.

Okeefe answered 18/5, 2018 at 11:41 Comment(0)
I
5

LocalesProvider is the component, not the context, so it doesn't have a Consumer property. I haven't tested it, but it will probably work if you export the created context:

export const LocalesContext = React.createContext(defaultStore);

change your imports to

import LocalesProvider, { LocalesContext } from './Providers/LocalesProvider.js';

and replace <LocalesProvider.Consumer> with <LocalesContext.Consumer>.

Idola answered 18/5, 2018 at 12:18 Comment(2)
That fixes the error but I have problems with state handling now. Doesn't the Context API trigger a componentWillChangeProps to the underlying children upon state update?Okeefe
Nevermind! I was using states instead of props, which was wrong in the Sidebar, since the Consumer passes data as props, not as state!Okeefe

© 2022 - 2024 — McMap. All rights reserved.