MDX-Bundler in Nextjs: Importing Components that import Components? - javascript

Using mdx-bundler, can I not import a file which imports another file?
Right now, in my .mdx file I do:
import MyComponent from './MyComponent'
This is my *mdx* file.
<MyComponent />
This works, when <MyComponent /> looks like this:
const MyComponent = () => {
return <div>Hello</div>
}
export default MyComponent
However, once I import something, it will fail. So when I change <MyComponent /> to this:
import AnotherBasicComponent from './AnotherBasicComponent'
const MyComponent = () => {
return <div>
<AnotherBasicComponent />
</div>
}
export default MyComponent
I get:
SyntaxError: Unexpected token '<'
at new Function ()
at getMDXExport (/Users/anton/projects/superdoo/node_modules/.pnpm/mdx-bundler#9.2.1_esbuild#0.15.18/node_modules/mdx-bundler/dist/client.js:44:14)
at getMDXComponent (/Users/anton/projects/superdoo/node_modules/.pnpm/mdx-bundler#9.2.1_esbuild#0.15.18/node_modules/mdx-bundler/dist/client.js:24:21)

Related

Trying to read an array from a file in React, but it comes up undefined

I'm new to React and am working on a small project. I'm trying to figure out why React won't read the data from my dataArray.js file. It comes up undefined when I console.log it. I made sure the data was being exported, the data was connected to the App.js file, and I have the data listed in the state.
I'm stumped as to what else to try.
import "./styles.css";
import { receiptsData } from "./dataArray";
import { Component } from "react";
import Receipt from "./components/Receipt";
class App extends Component {
state = {
receiptsData
};
render() {
console.log(receiptsData);
return (
<div className="App">
<Receipt receipts={this.state.receiptsData} />
</div>
);
}
}
export default App;
I have a copy on condesandbox.io: https://codesandbox.io/s/korillaapp-0pm5y3
I know I'm getting other errors as well, but I think it's tied to not being able to read the data from the dataArray.js file.
You have a default export in dataArray.js and named import in App.js.
So or do export const receiptsData = ... in dataArray.js, or import it as import receiptsData from "./dataArray"; in App.js
You exported your array as default export, so it should be imported like this :
import receiptsData from "./dataArray";
Or change your export like this :
export const receipts = [...]
1) Replace Receipt.js with below code
import ReceiptItem from "./ReceiptItem";
const Receipt = (props) => {
const { receipts } = props;
const receiptList = receipts.map((receipt, idx) => {
return(<div>
<ReceiptItem receipt={receipt} key={idx} />
</div>);
});
return (
<div>{receiptList}</div>
)
};
export default Receipt;
2) Add below line of code in the last in ReceiptItem.js
export default ReceiptItem;
3) You have default export receiptsData from dataArray.js so use receiptsData with {} in App.js

React render element is invalid while the import and export seem alright

Export and import of App seem alright. The error must be due to some mixing of names. This is my first react application. I have scoured various StackOverflow posts with the same error but I couldn't extract helpful insight from them. So, please be a bit elaborate.
Error:
Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object. 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 App.
Index.js :
import App from './App';
ReactDOM.render(<App /> , document.getElementById('root'));
App.jsx :
import React from 'react';
import { StreamChat } from 'stream-chat';
import { Chat } from 'stream-chat-react';
import Cookies from 'universal-cookie';
import { ChannelListContainer, ChannelContainer } from './components';
const App = () => {
return (
<div className="app__wrapper">
<Chat client={client} theme="team light">
<ChannelListContainer
/>
<ChannelContainer
/>
</Chat>
</div>
);
}
export default App;
index.js in ./components:
export { default as ChannelContainer } from './ChannelContainer';
export { default as ChannelListContainer } from './ChannelListContainer';
Because of you're using:
export { default as ChannelContainer } from "./ChannelContainer";
export { default as ChannelListContainer } from "./ChannelListContainer";
You need to export ChannelContainer from ./ChannelContainer like:
const ChannelListContainer = () => {
return <>ChannelListContainer</>;
};
export default ChannelListContainer;
And export ChannelListContainer from ./ChannelListContainer like:
const ChannelContainer = () => {
return <>ChannelContainer</>;
};
export default ChannelContainer;
Here's the sample based on your structure:

How to use #storybook/addon-docs/blocks <Props /> for Vue components in regular Stories?

We can extract the Vue component's Props into docs by writing this code in MDX
import { Props } from "#storybook/addon-docs/blocks";
import MyComponent from "./index.vue";
<Props of={MyComponent} />;
How can I achieve the same thing in regular stories?
import MyComponent from "./index.vue";
import { Props } from "#storybook/addon-docs/blocks";
// I don't know where to put `Props`
export default {
title: "MyComponent",
};
export const MyComponentStory = () => ({
components: { MyComponent },
template: `
<MyComponent />
`,
});
What should I do to achieve the same thing?

How to use "useRouter()" from next.js in a class component?

I was trying to get the queries from my url pattern like localhost:3000/post?loc=100 by using useRouter() from "next/router" and fetching some data using that id from my server. It worked when I used it in a Stateless Functional Component.
But the page showing "Invalid hook call" then. I tried calling getInitalProps() of a Stateless Functional Component, but it didn't work there either and showed the same error.
Is there any rule to use this method?
I was developing a front-end using React Library and Next.js Framework.
constructor(props) {
this.state = {
loc: useRouter().query.loc,
loaded: false
};
}
Hooks can be used only inside functional components, not inside classes. I would recommend to use withRouter HOC as per next.js documentation:
use the useRouter hook, or withRouter for class components.
Or see From Classes to Hooks if you want to switch to hooks.
In general, it's possible to create a wrapper functional component to pass custom hooks into class components via props (but not useful in this case):
const MyClassWithRouter = (props) => {
const router = useRouter()
return <MyClass {...props} router={router} />
}
class MyClass...
constructor(props) {
this.state = {
loc: props.router.query.loc,
loaded: false
};
}
withRouter example
https://stackoverflow.com/a/57029032/895245 mentioned it, but a newbie like me needed a bit more details. A more detailed/direct description would be:
Function component:
import { useRouter } from "next/router";
export default function Post() {
const router = useRouter();
return (
<div>{ router.query.id }</div>
)
}
Class component equivalent:
import { withRouter } from 'next/router'
import React from "react";
export default withRouter(class extends React.Component {
render() {
return (
<div>{ this.props.router.query.id }</div>
)
}
})
I tested this out more concretely as follows. First I took vercel/next-learn-starter/basics-final/pages/posts/[id].js and I hacked it to use the router:
diff --git a/basics-final/pages/posts/[id].js b/basics-final/pages/posts/[id].js
index 28faaad..52954d3 100644
--- a/basics-final/pages/posts/[id].js
+++ b/basics-final/pages/posts/[id].js
## -4,13 +4,17 ## import Head from 'next/head'
import Date from '../../components/date'
import utilStyles from '../../styles/utils.module.css'
+import { useRouter } from "next/router"
+
export default function Post({ postData }) {
+ const router = useRouter();
return (
<Layout>
<Head>
<title>{postData.title}</title>
</Head>
<article>
+ <div>router.query.id = {router.query.id}</div>
<h1 className={utilStyles.headingXl}>{postData.title}</h1>
<div className={utilStyles.lightText}>
<Date dateString={postData.date} />
Then, I ran it as:
git clone https://github.com/vercel/next-learn-starter
cd next-learn-starter
git checkout 5c2f8513a3dac5ba5b6c7621d8ea0dda881235ea
cd next-learn-starter
npm install
npm run dev
Now when I visit: http://localhost:3000/posts/ssg-ssr I see:
router.query.id = ssg-ssr
Then I converted it to the class equivalent:
import Layout from '../../components/layout'
import { getAllPostIds, getPostData } from '../../lib/posts'
import Head from 'next/head'
import Date from '../../components/date'
import utilStyles from '../../styles/utils.module.css'
import { withRouter } from 'next/router'
import React from "react"
export default withRouter(class extends React.Component {
render() {
return (
<Layout>
<Head>
<title>{this.props.postData.title}</title>
</Head>
<article>
<div>router.query.id = {this.props.router.query.id}</div>
<h1 className={utilStyles.headingXl}>{this.props.postData.title}</h1>
<div className={utilStyles.lightText}>
<Date dateString={this.props.postData.date} />
</div>
<div dangerouslySetInnerHTML={{ __html: this.props.postData.contentHtml }} />
</article>
</Layout>
)
}
})
export async function getStaticPaths() {
const paths = getAllPostIds()
return {
paths,
fallback: false
}
}
export async function getStaticProps({ params }) {
const postData = await getPostData(params.id)
return {
props: {
postData
}
}
}
and everything seemed to be unchanged.
Tested on Next.js 10.2.2.

React global component

I am coming from a vue.js background and I have just recently started looking into react.
I have a component: PageContent.jsx and I wish to use it without constantly having to import it to be able to use it inside the render function (JSX).
In vue it is possible to globalise a component using:
Vue.component(componentName, componentObject)
Is there anything similar in react?
Hmm, there isn't any kind of "global" component in React. Each component has to be imported or passed as a prop. You have a few options if you want to avoid adding an import to each file though:
1) Create a Higher Order Component that renders the PageContent and the wrapped component.
import PageContent from './PageContent';
const withPageContent = WrappedComponent => {
return class extends React.Component {
render () {
return (
<PageContent>
<WrappedComponent />
</PageContent>
)
}
}
};
export default withPageContent;
// Usage
import withPageContent from './withPageContent';
class MyComponent extends React.Component {
render () {
return (
<div>
I'm wrapped in PageContent!
</div>
)
}
}
export default withPageContent(MyComponent);
2) Pass PageContent as a prop to a component:
import PageContent from './PageContent';
export default class App extends React.Component {
render() {
return (
<React.Fragment>
<Child1 content={PageContent} />
<Child2 content={PageContent} />
</React.Fragment>
)
}
}
// Usage
export default class Child1 extends React.Component {
render () {
const PageContent = this.props.content;
return (
<PageContent>
I'm wrapped in PageContent!
</PageContent>
)
}
}
export default class Child2 extends React.Component {
render () {
const PageContent = this.props.content;
return (
<PageContent>
I'm wrapped in PageContent!
</PageContent>
)
}
}
I very much agree with Chase's answer.
Still if you need another approach you can use the context api. You can declare in the App root, or another nested components tree, a collection of components that you want to easily access.
Here is an example with the useContext hook, but hooks is not a must. The structure is the standard create-react-app structure.
The component we would like to access globally - src/deep/Header.js:
function Header() {
return (
<h1>
I am a global component
</h1>
);
}
export default Header;
The context creation - src/global-components-context.js:
import React from 'react';
const MyContext = React.createContext(null);
export default MyContext;
The grouping of the global-components - src/global-components.js:
import Header from './deep/Header';
const contextValue = {
Header,
};
export default contextValue;
The app init file - src/index.js:
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import MyContext from './global-components-context';
import contextValue from './global-component';
ReactDOM.render(
<MyContext.Provider value={contextValue}>
<App />
</MyContext.Provider>,
document.getElementById('root')
);
Using the component without importing it - src/App.js:
import { useContext } from 'react';
import globalComponent from './global-components-context';
function App() {
const Context = useContext(globalComponent);
return (
<div className="App">
<Context.Header />
</div>
);
}
export default App;
I think this is the most global components you can have in react. Note that you still need to import the context wherever you would like to use a global component.
Also one more disclaimer, global components are very hard to test and often to reason about. I believe that is why there is no standard solution for it in react.
Hope I could help

Categories

Resources