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.

1 Answer 1

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>.

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

2 Comments

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?
Nevermind! I was using states instead of props, which was wrong in the Sidebar, since the Consumer passes data as props, not as state!

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.