使用Context API实现父子及同级组件传值并解决渲染错误
解决Context API的TypeError及多组件数据消费问题
先来说你遇到的错误:
你看到的TypeError: render is not a function和Context Consumer需传入单个函数作为子元素警告,本质是同一个问题——你给AppContext.Consumer的子元素不是直接的渲染函数,而是套了一层<div>。Consumer的规则很明确:它必须直接接收一个函数作为子节点,这个函数会拿到Provider传递的上下文数据,然后返回要渲染的JSX。你现在把函数放在<div>里,Consumer识别不了这个函数,自然就报错了。
另外还有个隐藏问题:你的<AppContext.Provider>没有传递value属性,就算Consumer用对了,也拿不到App组件里的appData数据。
先修正当前的AppDataList组件和App组件
修正后的App.js
import React, { Component } from "react"; import "./App.css"; import AppDataList from "./components/AppDataList"; export const AppContext = React.createContext(); export default class App extends Component { constructor(props) { super(props); this.state = { appData: [] }; } fetchAppData() { fetch(`http://localhost:4000/AppDataList`) .then(res => res.json()) .then(res => { console.log(res) this.setState({ appData: res }); }); } componentDidMount() { this.fetchAppData(); } render() { return ( <div className="App"> {/* 必须通过value把要共享的数据传给Provider */} <AppContext.Provider value={this.state.appData}> <AppDataList /> </AppContext.Provider> </div> ); } }
修正后的AppDataList.js
import React, { Component } from "react"; import { AppContext } from "../App"; export default class AppDataList extends Component { render() { return ( <AppContext.Consumer> {/* 直接传递函数,参数就是Provider传来的appData */} {appData => ( <div> {/* 这里根据你的实际数据结构渲染,比如数组用map遍历 */} {appData.length > 0 ? ( appData.map((item, index) => ( <p key={index}>{JSON.stringify(item)}</p> )) ) : ( <p>数据加载中...</p> )} </div> )} </AppContext.Consumer> ); } }
实现多组件(Child1/Child2/Child3)消费数据的方案
只需要把这三个组件都放在AppContext.Provider的包裹范围内,然后每个组件按照正确的方式消费Context即可。
调整App.js包裹多组件
import React, { Component } from "react"; import "./App.css"; import Child1 from "./components/Child1"; import Child2 from "./components/Child2"; import Child3 from "./components/Child3"; export const AppContext = React.createContext(); export default class App extends Component { // 之前的state、fetchAppData、componentDidMount逻辑保持不变 render() { return ( <div className="App"> <AppContext.Provider value={this.state.appData}> {/* 把所有需要消费数据的子组件放在Provider内部 */} <Child1 /> <Child2 /> <Child3 /> </AppContext.Provider> </div> ); } }
子组件消费数据的两种方式
方式1:类组件用Consumer(和AppDataList一致)
以Child1为例:
// Child1.js import React, { Component } from "react"; import { AppContext } from "../App"; export default class Child1 extends Component { render() { return ( <AppContext.Consumer> {appData => ( <div className="child1"> <h3>Child1 展示数据</h3> {appData.length > 0 ? ( appData.map((item, idx) => ( <div key={idx}>{item.name}</div> {/* 替换成你数据里的实际字段 */} )) ) : ( <p>暂无数据</p> )} </div> )} </AppContext.Consumer> ); } }
方式2:函数组件用useContext钩子(更简洁)
如果你的子组件是函数组件,推荐用React的useContext钩子,代码更清爽,以Child2为例:
// Child2.js import React, { useContext } from "react"; import { AppContext } from "../App"; export default function Child2() { // 直接拿到上下文数据 const appData = useContext(AppContext); return ( <div className="child2"> <h3>Child2 展示数据</h3> {appData.length === 0 ? ( <p>加载中...</p> ) : ( <ul> {appData.map((item, idx) => ( <li key={idx}>{item.description}</li> {/* 替换成实际字段 */} ))} </ul> )} </div> ); }
关键要点总结
<AppContext.Provider>必须通过value属性传递需要共享的数据<AppContext.Consumer>的子元素必须是单个函数,不能嵌套其他元素,这个函数接收Provider的value作为参数,返回渲染的JSX- 函数组件优先使用
useContext钩子,代码更简洁易读
内容的提问来源于stack exchange,提问作者ram
相关产品推荐
相关产品推荐

