React.js Context API报错:元素类型无效问题排查求助
React Context API 错误排查:Element type is invalid
我帮你梳理了代码里的问题,这个错误的根源其实很清晰——你混淆了Context对象和Provider组件的关系。
问题原因
在LocalesProvider.js文件中,你创建了LocalesContext这个Context核心对象,但最终只导出了LocalesProvider组件,没有把LocalesContext也导出。而你在App和Sidebar组件里,错误地尝试用LocalesProvider.Consumer来消费Context数据,这是完全不对的:Consumer是属于React.createContext()返回的Context对象的属性,并不是Provider组件的属性,所以此时LocalesProvider.Consumer的值是undefined,React自然会抛出“元素类型无效”的错误。
修复步骤
1. 修改LocalesProvider.js,导出Context对象
把LocalesContext也导出,让其他组件能直接导入它来使用Consumer:
import React, { Component } from 'react'; import LocaleService from '../Services/LocaleService.js'; const defaultStore = { loaded: false, locales: [] }; export const LocalesContext = React.createContext(defaultStore); // 新增export导出Context 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;
2. 修正App.js里的Consumer引用
导入LocalesContext,用LocalesContext.Consumer替代错误的LocalesProvider.Consumer:
import React, { Component } from 'react'; import Sidebar from './Sidebar.js'; import Topbar from './Topbar.js'; import Content from './Content.js'; // 同时导入默认导出的Provider和命名导出的Context import LocalesProvider, { LocalesContext } 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> <LocalesContext.Consumer> {/* 修正为Context对象的Consumer */} { data => ( <Sidebar isReady={data.loaded} locales={data.locales}/> ) } </LocalesContext.Consumer> </div> <main className="column" style={{overflow: 'auto', position: 'relative'}}> <Content/> </main> </section> </section> </div> ); } else { return ('Loading...'); } } } export default App;
3. 修正Sidebar.js里的Consumer引用
同样导入LocalesContext,替换错误的Consumer引用:
import React, { Component } from 'react'; // 导入命名导出的Context import { LocalesContext } 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> <LocalesContext.Consumer> {/* 修正为Context对象的Consumer */} {locales => ( this.buildLocaleLinks(locales, 'translate') )} </LocalesContext.Consumer> </ul> </li> <li>Validate</li> <li> <ul> <LocalesContext.Consumer> {/* 修正为Context对象的Consumer */} {locales => ( this.buildLocaleLinks(locales, 'validate') )} </LocalesContext.Consumer> </ul> </li> </ul> </aside> </HashRouter> ); } } export default Sidebar;
额外优化小建议
- 你在Sidebar里既通过props接收
locales和isReady,又在组件内部用Consumer获取locales,有点冗余,可以考虑统一用一种方式获取数据。 load方法里可以用箭头函数替代that = this的写法,让代码更简洁:
load() { const service = new LocaleService(); service.fetch().then((locales) => { this.setState({ locales, loaded: true }); }); }
内容的提问来源于stack exchange,提问作者GiamPy
相关产品推荐
相关产品推荐

