React error Objects are not valid as a React child (found: object with keys {$$typeof, type, compare)

1.8k views Asked by At

I got a legacy project of Meteor and React and I try to make it work. But I got an error at one point. Can anyone help me to resolve following error?

Uncaught Error: Objects are not valid as a React child (found: object with keys {$$typeof, type, compare}). If you meant to render a collection of children, use an array instead.
    in Unknown
    in main (created by Basic)
    in Basic (created by Context.Consumer)
    in Content (created by PageLayout)
    in section (created by BasicLayout)
    in BasicLayout (created by Context.Consumer)
    in Layout (created by PageLayout)
    in section (created by BasicLayout)
    in BasicLayout (created by Context.Consumer)
    in Layout (created by PageLayout)
    in section (created by BasicLayout)
    in BasicLayout (created by Context.Consumer)
    in Layout (created by PageLayout)
    in LocaleProvider (created by Context.Consumer)
    in LocaleReceiver (created by ConfigProvider)
    in ConfigProvider (created by PageLayout)
    in PageLayout (created by ForwardRef)
    in ForwardRef
    in ForwardRef
    at throwOnInvalidObjectType (modules.js:64084)
    at reconcileChildFibers (modules.js:64984)
    at reconcileChildren (modules.js:67433)
    at mountIndeterminateComponent (modules.js:68213)
    at beginWork (modules.js:69267)
    at HTMLUnknownElement.callCallback (modules.js:50859)
    at Object.invokeGuardedCallbackDev (modules.js:50908)
    at invokeGuardedCallback (modules.js:50963)
    at beginWork$1 (modules.js:73874)
    at performUnitOfWork (modules.js:72828)

Within the secure.js the PostCategories are mounted like this

ContentRouter.route('/content-creator', {
  name: 'Content Creator',
  icon: 'solution',
  permission: 'main-navigation.view.module.posts',
  className: 'posts',
  visible: true,
  action: () => {
    mount(PageLayout, { content:  <PostCategories /> });
  },
});

The PageLayout looks like this :

import { Meteor } from 'meteor/meteor';
import { withTracker } from 'meteor/react-meteor-data';
import React, { Component } from 'react';
import { FlowRouter } from 'meteor/ostrio:flow-router-extra';
import Konami from 'react-konami-code';
import deDE from 'antd/lib/locale-provider/de_DE';
import moment from 'moment';
import 'moment/locale/de';

import {
  ConfigProvider, Layout, Menu, Icon, Steps, Badge, Button, message,
} from 'antd';
import { Workspaces } from '../../../api/workspaces/collection';
import DoneStepper from './components/doneStepper';
import { DASHBOARD_VERSION } from '../../../helpers/Constants';


const {
  Header, Content, Sider, Footer,
} = Layout;

moment.locale('de');

class PageLayout extends Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false, error: null };
  }

  logout = () => {
    Meteor.logout(() => {
      FlowRouter.go('/anmelden');
    });
  };

  renderMenuItems = () => {
    const groupName = FlowRouter.current().route.group.name;

    const items = FlowRouter._routes.map((route) => {
      const isSecure = route && route.group && route.group.parent && route.group.parent && route.group.parent.name === 'secure';
      const isVisible = route && route.options && route.options.visible && route.options.visible === true;
      const isActive = groupName === route.group.name;

      let active = null;

      if (isActive) {
        active = 'ant-menu-item-selected';
      }

      return (
        isVisible
        && isSecure && (
          <Menu.Item key={route.path} style={route.options.style} className={[route.options.className, active]}>
            <a href={route.path}>
              <Icon type={route.options.icon} />
              <span className="nav-text">{route.name}</span>
            </a>
          </Menu.Item>
        )
      );
    });
    return items;
  };

  easterEgg = () => {
    message.success('Development Mode enabled');
  };

  handleResetWorkspace = () => {
    const { user } = this.props;
    Meteor.call('workspace.reset', user.profile.workspace, (error) => {
      if (error) {
        console.log(error);
        message.error('error');
      } else {
        message.success('success');
      }
    });
  };

  static getDerivedStateFromError(error) {
    // Update state so the next render will show the fallback UI.
    return { hasError: true, error: error };
  }

  componentDidCatch(error, errorInfo) {
    // You can also log the error to an error reporting service
    console.log(error, errorInfo);
  }

  render() {
    if (this.state.hasError) {
      // You can render any custom fallback UI
      console.log('ERROR IS:');
      console.log(this.state.error);
      return <h1>Something went wrong.</h1>;

    }

    const { ready, user, workspace, content } = this.props;
    if (ready) {
      if (workspace && workspace.appName && workspace.appName.name) {
        document.title = `App - ${workspace.appName.name}`;
      } else {
        document.title = 'App';
      }
    }

    return (
      <ConfigProvider locale={deDE}>
        <Layout className="manager-dashboard">
          <Header className="header">
            <div className="logo__area">
              <a href="/">
                <img className="logo" src="/logo.svg" alt="cms" />
              </a>
            </div>

            <DoneStepper />
            <Konami action={this.easterEgg}>
              <Button type="primary" onClick={() => this.handleResetWorkspace()}>
                <Icon type="delete" size="small" />
                {'reset workspace'}
              </Button>
            </Konami>

            <div className="member">
              <a onClick={this.logout}>
                {'Abmelden '}
                <Icon type="logout" />
              </a>
            </div>
          </Header>
          <Layout>
            <Sider width={200} style={{ background: '#fff' }}>
              <div className="fixed-sidebar">
                <Menu
                  defaultSelectedKeys={[FlowRouter.current().route.path]}
                  selectedKeys={[FlowRouter.current().route.path]}
                >
                  {this.renderMenuItems()}
                </Menu>
              </div>
            </Sider>
            <Layout className="dashboard__content">
              <Content
                style={{
                  background: '#fff',
                  padding: 20,
                  minHeight: '100vh',
                }}
              >
                {content}
              </Content>
              <Footer>{`App - Version ${DASHBOARD_VERSION}`}</Footer>
            </Layout>
          </Layout>
        </Layout>
      </ConfigProvider>
    );
  }
}
export default withTracker(() => {
  const user = Meteor.user();
  const handleWorkspace = Meteor.subscribe('workspaces.one', {
    query: { _id: user.profile.workspace },
    fields: { appName: 1 },
  });

  const ready = handleWorkspace.ready();
  let workspace = null;

  if (ready) {
    workspace = Workspaces.findOne({ _id: user.profile.workspace }, { fields: { appName: 1 } });
  }

  return {
    ready,
    user,
    workspace,
  };
})(PageLayout);

And the PostCategories look like this:

/* eslint-disable jsx-a11y/click-events-have-key-events */
import { Meteor } from 'meteor/meteor';
import { withTracker } from 'meteor/react-meteor-data';
import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';
import { FlowRouter } from 'meteor/ostrio:flow-router-extra';
import { PostCategories } from '/imports/api/posts/collection';
import React, { Component } from 'react';
import Role from '/imports/helpers/Role';
import CreatePostCategory from '/imports/ui/secure/partials/forms/createPostCategory.js';
import {
  Card, Button, message, Row, Col, Icon, Switch, Popover,
} from 'antd';

import Text from '../../components/Text.js';
import Preview from '../../components/preview/index.js';
import CustomSpinner from '../../components/custom-spinner';

const DragIndicator = () => (
  <svg viewBox="0 0 14 5" height="15" width="15" xmlns="http://www.w3.org/2000/svg">
    <path d="m0 0h14v.83823529h-14z" />
    <path d="m0 2h14v.83823529h-14z" />
    <path d="m0 4h14v.83823529h-14z" />
  </svg>
);

const getItemStyle = (isDragging, draggableStyle) => ({
  userSelect: 'none',
  ...draggableStyle,
});

const reorder = (list, startIndex, endIndex) => {
  const result = Array.from(list);
  const [removed] = result.splice(startIndex, 1);
  result.splice(endIndex, 0, removed);

  return result;
};

class Categories extends Component {
  constructor(props) {
    super(props);
    this.state = {
      categories: props.categories,
      createCategory: false,
      justReorder: false,
      deletePopoverVisible: {},
    };
  }

  static getDerivedStateFromProps(nextProps, prevState) {
    if (nextProps.ready && !prevState.justReorder) {
      prevState.categories = nextProps.categories;
      prevState.justReorder = false;
    } else {
      prevState.justReorder = false;
    }
    return prevState;
  }

  handleCategoryState = (id, checked) => {
    Meteor.call('post.category.state', id, checked, (error) => {
      if (error) {
        console.log(error);
        message.error('error');
      } else {
        message.success('success');
      }
    });
  };

  handleDeletePopoverVisibleChange = (change, id) => {
    this.state.deletePopoverVisible[id] = change;
    this.setState(this.state);
  };

  handleDelete = (e, id) => {
    e.preventDefault();
    Meteor.call('post.category.delete', id, (error) => {
      if (error) {
        console.log(error);
        message.error('error');
      } else {
        message.success('success');
      }
    });
  };

  handleCreateCategory = (e) => {
    e.preventDefault();
    const form = this.createCategoryForm;
    form.validateFieldsAndScroll((err, values) => {
      if (err) {
        return;
      }
      values.workspace = this.props.user.profile.workspace;
      values.index = this.props.categories.length + 1;
      Meteor.call('post.category.insert', values, (error) => {
        if (error) {
          console.log(error);
          message.error('error');
        } else {
          message.success('success');
          form.resetFields();
          this.setState({ createCategory: false, categoryId: null });
        }
      });
    });
  };

  onDragEnd = (data) => {
    console.log(data);
    
    const { source, destination, draggableId } = data;
    if (!destination) {
      return;
    }
    if (source.droppableId === destination.droppableId) {
      this.state.categories = reorder(this.state.categories, data.source.index, data.destination.index);
      this.state.categories.forEach((category, idx) => (category.index = idx));
      this.state.justReorder = true;
      this.setState(this.state, () => {
        Meteor.call('post.category.reorder', { categories: this.state.categories }, (error, result) => {
          if (error) {
            console.log(error);
            message.error('error.');
          }
        });
      });
    }
  };

  render() {
    const { ready, categories } = this.props;
    const { categories: stateCategories, deletePopoverVisible, createCategory } = this.state;
    let content = <CustomSpinner />;
    if (ready) {
      content = (
        <Col>
          <Row gutter={40}>
            <Col xs={24} lg={16} xxl={18}>
              {!Array.isArray(categories) ? (
                <Row gutter={40}>
                  <Col
                    xs={{ span: 24 }}
                    lg={{ span: 18, offset: 3 }}
                    xl={{ span: 12, offset: 6 }}
                    style={{ textAlign: 'center' }}
                  >
                    <img src="/emptystates/contentcreator.svg" alt="empty dashboard" />
                  </Col>
                  <Col
                    xs={{ span: 24 }}
                    lg={{ span: 18, offset: 3 }}
                    xl={{ span: 16, offset: 4 }}
                    style={{ textAlign: 'center', marginTop: '40px' }}
                  >
                    <h2>Head</h2>
                    <p>
                   Text
                    </p>
                    <Button
                      className="button--center"
                      size="large"
                      type="primary"
                      onClick={() => this.setState({ createCategory: true })}
                    >
                      Los geht's
                      <Icon type="right" />
                    </Button>
                  </Col>
                </Row>
              ) : (
                <div>
                  <div className="page-title">
                    <h1>Content Creator</h1>
                    <p>
                    Text
                    </p>
                  </div>
                  <Row gutter={40}>
                    <Col xs={24}>
                      <div className="shadow-wrapper">
                        <DragDropContext
                          onDragEnd={this.onDragEnd}
                        >
                          <Droppable droppableId="droppable">
                            {(provided, snapshot) => (
                              <div {...provided.droppableProps} ref={provided.innerRef}>
                                {stateCategories.map((category, index) => (
                                  <Draggable key={category._id} draggableId={category._id} index={index}>
                                    {(provided, snapshot) => (
                                      <div
                                        ref={provided.innerRef}
                                        {...provided.draggableProps}
                                        {...provided.dragHandleProps}
                                        style={getItemStyle(snapshot.isDragging, provided.draggableProps.style)}
                                      >
                                        <Card className="card--list-view">
                                          <div>
                                            <div className="card__title">
                                              <span {...provided.dragHandleProps}>
                                                <Icon
                                                  component={DragIndicator}
                                                  style={{
                                                    paddingRight: '20px',
                                                    paddingTop: '6px',
                                                  }}
                                                />
                                              </span>
                                              <a href={`/content-creator/kategorie/${category._id}`}>
                                                {category.title}
                                              </a>
                                            </div>
                                            <div className="card__edit">
                                              <span>
                                                <a
                                                  href={`/content-creator/neuen-artikel-anlegen?id=${category._id}`}
                                                  onClick={() => FlowRouter.go(
                                                    '/content-creator/neuen-artikel-anlegen',
                                                    {},
                                                    { id: category._id },
                                                  )
                                                  }
                                                >
                                                  <Icon type="folder-add" />
                                                  {' Artikel hinzufügen'}
                                                </a>
                                              </span>

                                              <Text
                                                label="Umbenennen der Kategorie"
                                                required
                                                message="Bitte tragen Sie einen Kategorietitel ein."
                                                initialValue={category.title}
                                                render={(
                                                  <span>
                                                    <Icon
                                                      style={{
                                                        marginRight: 5,
                                                      }}
                                                      type="edit"
                                                    />
                                                    Umbenennen
                                                  </span>
)}
                                                onValid={(result) => {
                                                  Meteor.call(
                                                    'post.category.edit',
                                                    category._id,
                                                    { title: result },
                                                    (e) => {
                                                      if (e) {
                                                        console.log(e);
                                                        message.error('Bei dieser Aktion ist ein Fehler aufgetreten');
                                                      } else {
                                                        message.success('Kategorie Titel erfolgreich erstellt');
                                                      }
                                                    },
                                                  );
                                                }}
                                              />
                                              <Popover
                                                content={(
                                                  <div className="manager-dashboard">
                                                    <div
                                                      className="page__actions"
                                                      style={{ marginBottom: 0, marginTop: 0 }}
                                                    >
                                                      <Button
                                                        type="danger"
                                                        onClick={() => this.handleDeletePopoverVisibleChange(false, category._id)
                                                        }
                                                      >
                                                        <Icon type="close" />
                                                        {' Abbrechen'}
                                                      </Button>
                                                      <Button
                                                        type="primary"
                                                        style={{ marginLeft: '10px' }}
                                                        onClick={event => this.handleDelete(event, category._id)}
                                                      >
                                                        {'Löschen '}
                                                        <Icon type="delete" />
                                                      </Button>
                                                    </div>
                                                  </div>
)}
                                                title="Diese Kategorie wirklich löschen?"
                                                trigger="click"
                                                visible={deletePopoverVisible[category._id]}
                                                onVisibleChange={change => this.handleDeletePopoverVisibleChange(change, category._id)
                                                }
                                              >
                                                <Icon type="delete" />
                                                {' Löschen'}
                                              </Popover>

                                              <Switch
                                                style={{ float: 'right' }}
                                                defaultChecked={category.state}
                                                onChange={checked => this.handleCategoryState(category._id, checked)}
                                                checkedChildren="Öffentlich"
                                                unCheckedChildren="Entwurf"
                                              />
                                            </div>
                                          </div>
                                        </Card>
                                      </div>
                                    )}
                                  </Draggable>
                                ))}
                                {provided.placeholder}
                              </div>
                            )}
                          </Droppable>
                        </DragDropContext>
                        {Role.hasPermission('product.category.create') && (
                          <Card
                            className="card--list-view add-new-card"
                            onClick={() => this.setState({ createCategory: true })}
                          >
                            <div className="card__title">
                              <a>+ Neue Kategorie hinzufügen</a>
                            </div>
                          </Card>
                        )}
                      </div>
                    </Col>
                  </Row>
                </div>
              )}

              {createCategory && (
                <CreatePostCategory
                  ref={(form) => {
                    this.createCategoryForm = form;
                  }}
                  visible={createCategory}
                  onCancel={() => this.setState({ createCategory: false })}
                  onCreate={this.handleCreateCategory}
                />
              )}
            </Col>
            <Col xs={24} sm={12} lg={8} xxl={6}>
              <Preview type="categories" />
            </Col>
          </Row>
        </Col>
      );
    }
    return content;
  }
}

export default withTracker(() => {
  const user = Meteor.user();
  const handleCategories = Meteor.subscribe('posts.categories.all', {
    query: { workspace: user.profile.workspace },
    sort: { index: 1, title: 1 },
    fields: {
      title: 1, state: 1, index: 1, workspace: 1,
    },
  });
  const ready = handleCategories.ready();
  let categories = null;
  if (ready) {
    categories = PostCategories.find(
      { workspace: user.profile.workspace },
      {
        sort: { index: 1, title: 1 },
        fields: {
          title: 1, state: 1, index: 1, workspace: 1,
        },
      },
    ).fetch();
  }

  return {
    user,
    ready,
    categories,
  };
}, Categories);

I am completly clueless what is going wrong, maybe somebody could help me out

1

There are 1 answers

1
Mikkel On

React components are functions, and this error message comes about because you are passing in an object rather than a function.

That may be enough for you to find the problem now. It is helpful for us if you can do some narrowing down of the problem, rather than dumping a large amount of code - it becomes too expensive (time consuming) for us to help you if we have to pore through hundreds of lines of code.