React中使用Provider标签引发空白页面问题求助
问题描述
使用React.js结合Redux开发网页,用<Provider>标签包裹组件后页面变为空白,React开发者工具显示空白,控制台报错:
Uncaught Error: Expected the reducer to be a function.
at createStore (createStore.js:55:1)
at ./src/index.js (index.js:9:1)
at options.factory (react refresh:6:1)
at webpack_require (bootstrap:24:1)
at startup:7:1
at startup:7:1
相关代码如下:
Main.js
import Title from './Title' import Photowall from './Photowall' import AddPhoto from './AddPhoto' import { Route } from 'react-router-dom' class Main extends Component{ constructor(){ super() this.state= { posts : [{ id: "0", description: "beautiful landscape", imageLink: "https://image.jimcdn.com/app/cms/image/transf/none/path/sa6549607c78f5c11/image/i4eeacaa2dbf12d6d/version/1490299332/most-beautiful-landscapes-in-europe-lofoten-european-best-destinations-copyright-iakov-kalinin.jpg" + "3919321_1443393332_n.jpg" }, { id: "1", description: "Aliens???", imageLink: "https://s3.india.com/wp-content/uploads/2017/12/rocket.jpg" }, { id: "2", description: "On a vacation!", imageLink: "https://fm.cnbc.com/applications/cnbc.com/resources/img/editorial/2017/08/24/104670887-VacationExplainsTHUMBWEB.1910x1000.jpg" }], } this.removePhoto=this.removePhoto.bind(this) } removePhoto(postRemoved){ console.log(postRemoved.description) this.setState((state) => ({ posts: state.posts.filter(post => post !== postRemoved) })) } addPhoto(postSubmitted){ this.setState(state => ({ posts:state.posts.concat([postSubmitted]) })) } componentDidMount(){ } componentDidUpdate(prevProps,prevState){ console.log(prevState.posts) console.log(this.state) } render() { return ( <div> <Route exact path="/" render={() => ( <div> <Title title={'Photowall'}/> <Photowall posts={this.state.posts} onRemovePhoto={this.removePhoto} onNavigate={this.navigate}/> </div> )}/> <Route path="/AddPhoto" render={({history})=>( <AddPhoto onAddPhoto={(addedPost)=>{ this.addPhoto(addedPost) history.push('/') }}/> )}/> </div> ) } } export default Main
AddPhoto.js
import React, { Component } from "react" class AddPhoto extends Component{ constructor(){ super() this.handleSubmit=this.handleSubmit.bind(this) } handleSubmit(event){ event.preventDefault(); const imageLink = event.target.elements.link.value const description = event.target.elements.description.value const post = { id:0, description:description, imageLink:imageLink } if(description && imageLink){ this.props.onAddPhoto(post) } } render(){ return( <div> <h1>Photowall</h1> <div className="form"> <form onSubmit={this.handleSubmit}> <input type="text" placeholder="Link" name="link"/> <input type="text" placeholder="Description" name="description"/> <button>Post</button> </form> </div> </div> ) } } export default AddPhoto
Photo.js
import React, {Component} from "react"; import PropTypes from 'prop-types' function Photo(props){ const post =props.post return <figure className="figure"> <img className="photo" src={post.imageLink} alt={post.description}/> <figcaption><p>{post.description}</p></figcaption> <div className="button-container"> <button className="remove-button" onClick={()=>{ props.onRemovePhoto(post); }}> Remove </button> </div> </figure> } Photo.propTypes={ post: PropTypes.object.isRequired, onRemovePhoto:PropTypes.func.isRequired } export default Photo
Photowall.js
import React,{ Component} from 'react' import Photo from './Photo' import PropTypes from 'prop-types' import {Link} from 'react-router-dom' function Photowall(props){ return <div> <Link className='addIcon' to='/AddPhoto' ><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBkPSJNMTIgMGMtNi42MjcgMC0xMiA1LjM3My0xMiAxMnM1LjM3MyAxMiAxMiAxMiAxMi01LjM3MyAxMi0xMi01LjM3My0xMi0xMi0xMnptNyAxNGgtNXY1aC00di01aC01di00aDV2LTVoNHY1aDV2NHoiLz48L3N2Zz4="></img> </Link> {/*<button onClick={props.onNavigate} className='addIcon'> + </button>*/} <div className='photo-grid'> {props.posts.map((post, index) => <Photo key={index} post={post} onRemovePhoto={props.onRemovePhoto}/>)} </div> </div> } Photowall.propTypes={ posts: PropTypes.array.isRequired, onRemovePhoto:PropTypes.func.isRequired } export default Photowall
index.js
import React, { Component } from 'react'; import ReactDOM from 'react-dom'; import Main from './Components/Main' import './styles/stylesheet.css' import {BrowserRouter} from 'react-router-dom' import {createStore} from 'redux' import rootReducer from './redux/reducer' import {Provider} from "react-redux"; const store = createStore(rootReducer) ReactDOM.render(<Provider store={store}><BrowserRouter><Main/></BrowserRouter></Provider>, document.getElementById('root'));
reducer.js
import posts from '../data/posts' const postReducer = function posts(state = posts,action){ return state } export default postReducer
问题分析与解决
报错Expected the reducer to be a function的核心原因是createStore接收到的rootReducer并非预期的函数,具体问题出在reducer.js中:
- 变量命名冲突:你导入了名为
posts的数据数组,同时又将reducer函数命名为posts,这会导致函数被数组覆盖,最终导出的是数组而非reducer函数。
修正步骤:
修改reducer.js中的函数名,避免与导入的数组重名:
import posts from '../data/posts' // 修改函数名,避免和导入的posts数组冲突 const postReducer = function(state = posts, action){ return state } export default postReducer
同时确认../data/posts文件存在且正确导出数组,若该文件缺失或导出错误,也会导致reducer初始化失败。
额外优化建议:
- 后续可逐步将
Main组件的state迁移至Redux store,通过connect或useSelector/useDispatch来管理数据; - 给新增的post生成唯一ID(如时间戳、uuid库),避免ID重复问题。
内容的提问来源于stack exchange,提问作者ronin
相关产品推荐
相关产品推荐

