1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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. Check the render method of `TopicDetail`. in TopicDetail (created by inject-TopicDetail) in inject-TopicDetail (created by WithStyles(inject-TopicDetail)) in WithStyles(inject-TopicDetail) (created by Route) in Route (created by _default) in _default (created by App) in App (created by Main) in Main in MuiThemeProvider in Router (created by BrowserRouter) in BrowserRouter in Provider in AppContainer |
发生原因一般是引用了无效的组件,如果组件确实正确,看下引用的组件是否正常导出:(export default)
但是并没有找到哪里出了问题
TopicDetail 源代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | import React from 'react' import PropTypes from 'prop-types' import marked from 'marked' import Helmet from 'react-helmet' import { inject, observer, } from 'mobx-react' import green from '@material-ui/core/colors/green' import { withStyles } from '@material-ui/core/styles' import Paper from '@material-ui/core/Paper' import { CircularProgress } from '@material-ui/core/CircularProgress' import Container from '../layout/container' import { topicDetailStyle } from './styles' import Reply from './reply' import TopicStore from '../../store/topic-store' @inject((stores) => { return { topicStore: stores.topicStore, } }) @observer class TopicDetail extends React.Component { componentDidMount() { const id = this.getTopicId() this.props.topicStore.getTopicDetail( id ) } getTopicId() { return this.props.match.params. id } render() { const { classes, } = this.props const id = this.getTopicId() // 通过react-router生成的match对象获取url的params参数 const topic = this.props.topicStore.detailMap[ id ] // const topic = this.props.topicStore.topics[1] if (!topic) { // topic 不存在加载 loading return ( <Container> <section className={classes.loadingContainer}> <CircularProgress style={{ color: green[500] }} size={100} /> < /section > < /Container > ) } // 把内容放到 p 标签下,但是是直接塞入 html,不转义html标签 return [ <Container> <Helmet> <title>{topic.title}< /title > < /Helmet > <header className={classes.header}> <h3>{topic.title}< /h3 > < /header > <section className={classes.body}> <p dangerouslySetInnerHTML={{ __html: marked(topic.content) }} /> {/* eslint-disable-line */} < /section > < /Container >, <Paper elevation={4} className={classes.replies}> <header className={classes.replyHeader}> <span>{`${topic.reply_count} 回复`}< /span > <span>{`最新回复 ${topic.last_reply_at}`}< /span > < /header > <section> { topic.replies.map(reply => <Reply reply={reply} key={reply. id } />) } < /section > < /Paper >, ] } } TopicDetail.propTypes = { topicStore: PropTypes.instanceOf(TopicStore), classes: PropTypes.object.isRequired, match: PropTypes.object.isRequired, } export default withStyles(topicDetailStyle)(TopicDetail) |
topic stre 源代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | import { observable, toJS, computed, action, extendObservable, } from 'mobx' import { topicSchema } from '../utils/variable-define' import { get } from '../utils/http' const createTopic = (topic) => { return Object.assign({}, topicSchema, topic) // 返回所有字段都有定义的topic对象 } /** * 为了以后扩展更加容易,创建一个类,让每个话题都放到这个类中 * 即变成类的实例,更好的控制话题 * 让数据使用 mobx 具有的特性 */ export class Topic { // 创建 topic 的时候就是所有有定义的字段都添加 observable 属性并附加this constructor(data) { extendObservable( this , data) // 把数据直接扩展到 this 上并使用 observeable } @observable syncing = false // 是否异步操作请求数据,在组件中反应正在加载的操作 } class TopicStore { @observable topics @observable details // 数组 @observable syncing // 是否正在处理数据请求 // constructor({ syncing, topics, details } = { syncing: false, topics: [], details: [] }) { constructor({ syncing = false , topics = [], details = [] } = {}) { // 整个对象默认等于 空对象 // 初始化数据,设置默认值 this .syncing = syncing this .topics = topics.map(topic => new Topic(createTopic(topic))) // 数组使用map this .details = details.map(topic => new Topic(createTopic(topic))) // 数组使用map } /** * 上面是 topic 的定义 * 下面获取 topic 数据 */ addTopic(topic) { // 往 topics 里加入新的 topic 对象 this .topics.push( new Topic(createTopic(topic))) } @computed get detailMap() { // 方便获取某个 id 下的 detail return this .details.reduce((result, detail) => { result[detail.id] = detail // eslint-disable-line return result }, {}) } @action fetchTopics(tab) { this .syncing = true // 开始请求数据之前设置为 true 表示正在异步获取数据 this .topics = [] // 获取之前先清空 return new Promise((resolve, reject) => { get( '/topics' , { mdrender: false , // markdown 字符串是否转义成 html 字符串,不转义还可以继续编辑 markdown tab, }).then((resp) => { if (resp.success) { // 把拿到的所有 topic 都放到 this.topics 里 resp.data.forEach((topic) => { this .addTopic(topic) }) resolve() // 数据获取成功 } else { reject() // 数据获取失败 } this .syncing = false // 数据获取结束 }). catch ((err) => { // 出现任何异常 reject(err) this .syncing = false // 出现异常数据获取结束 }) }) } @action getTopicDetail(id) { return new Promise((resolve, reject) => { if ( this .detailMap[id]) { // 有数据,即已经获取过该id的详情数据 resolve( this .detailMap[id]) } else { // 没有就获取 get(`/topic/${id}`, { mdrender: false , // 不要转化 markdown 格式 }).then((resp) => { if (resp.success) { const topic = new Topic(createTopic(resp.data)) // 新建一个 topic 对象 this .details.push(topic) // 获取的数据放入 details 数组中 resolve(topic) // 就可以很方便的根据id去 detailMap 获取有详情的话题对象 } else { reject() } }). catch (reject) } }) } toJson() { return { topics: toJS( this .topics), syncing: this .syncing, details: toJS( this .details), } } } export default TopicStore |