Import markdown files dynamically with Vite - reactjs

I'm looking for a solution that lets me render markdown dynamically, based on the query string. At the moment I render markdown like this, in React + Vite:
import some other stuff...
import { useParams } from 'react-router-dom';
import { ReactComponent } from '../content/blog1.md';
const BlogPost = () => {
const params = useParams();
return (
<Base>
<PageTitle title={`title of blog ${params.blogId}`} />
<ReactComponent />
</Base>
);
};
export default BlogPost;
What I would like is to pass the blogId to the import path like:
import { ReactComponent } from `../content/blog${params.blogId}.md`
Such that the correct file is imported on each /blog/* route. I tried this by lazy loading the markdown like:
const path = `../content/blog${params.blogId}.md`;
const Markdown = React.lazy(() => import(path));
But this raises errors and when I log Markdown I see
{$$typeof: Symbol(react.lazy), _payload: {…}, _init: ƒ}
$$typeof: Symbol(react.lazy)
_init: ƒ lazyInitializer(payload)
_payload: {_status: -1, _result: ƒ}
Which appears to be empty. What would be a solution to accomplish this?

I had also issue when importing assets. In my case .md import failed.
import Article from './assets/article.md';
Failed to parse source for import analysis because the content
contains invalid JS syntax. You may need to install appropriate
plugins to handle the .md file format, or if it's an asset, add
"**/*.md" to assetsInclude in your configuration
So I solved it adding assetsInclude: ['**/*.md'] setting to vite.config.js (for my .md files)
export default defineConfig({
...
plugins: [react()],
assetsInclude: ['**/*.md']
...
})
Eventually this started to work:
import("./assets/article.md").then(res => {
fetch(res.default)
.then(response => response.text())
.then(text => console.log(text))
})
Docs are here:https://vitejs.dev/guide/assets.html
Hope it helps.
Cheers.

Related

Gatsby dynamic routes: 404 instead of component, can't configure

I made a simple site with Gatsby.js and can't configure dynamic routes.
I have index.js page (was automatically created by react), that looks like:
import * as React from 'react'
const IndexPage = () => {
return (
<Layout
pageTitle="Home Page"
>
Some text for my main page
</Layout>
)
}
export const Head = () => <title>Home Page</title>
export default IndexPage
Layout components includes Header that looks like:
import React, { useState, useEffect } from 'react';
import { Link } from 'gatsby';
const Header = () => {
return (
<Wrapper style={{ *some styles* }}>
<Link to="/">Index</Link>
<Link to="/projects">Projects</Link >
</Wrapper>
)
};
export default Header;
I have my Projects page that looks like this:
import * as React from 'react'
import { BrowserRouter, Route, Routes } from 'react-router-dom'
import Layout from '../layout'
const Projects = () => {
return (
<BrowserRouter>
<Layout>
<Wrapper>
<Routes>
<Route path="projects/:projectID/" component={ProjectDetails} />
</Routes>
<MyProjectLink to="/projects/1">
Project 1
</MyProjectLink>
<MyProjectLink to="/projects/2">
Project 2
</MyProjectLink>
</Wrapper>
</Layout>
</BrowserRouter>
)
}
export const Head = () => <title>Our Projects</title>
export default Projects
And I have my ProjectDetails component:
import React from 'react';
import { useParams } from 'react-router';
import Layout from '../../pages/layout';
const ProjectDetails = () => {
const { projectID } = useParams();
return (
<Layout>
<Wrapper>
<h2>Project {projectID}</h2>
</Wrapper>
</Layout>
);
}
export default ProjectDetails;
The problem is that when I navigate to localhost:8000/projects/1 (or "2", or "3", or "100500") I see a 404 page instead of my ProjectDetails component.
I've tried wrapping the index page to BrowserRouter and move the routes with my route there, but that's a dumb idea in my opinion (and it doesnt work).
Did I miss something? Some features of Gatsby (v5) that I don't know about? I'm new to Getsby and to be honest I thought that dynamic routes here work the same way as in React Router.
Gatsby extends its routing from React, however, the way to create routes is slightly different.
As far as I understand your code, you are trying to create a template page for projects: this can be simply done by creating a file inside /templates folder. A simple component like this should work:
const Projects = ({ data }) => {
return (
<Layout>
<Wrapper></Wrapper>
</Layout>
);
};
export const Head = () => <title>Our Projects</title>
export default Projects
This template, as long as you use it when creating pages (using either gatsby-node.js or File System Route API) will be used to hold each specific project data.
Each project data will be queried using GraphQL and held inside props.data but without knowing your source (can be markdown, JSON, CMS, etc) I can't provide a sample query.
Once Gatsby infers its GraphQL nodes from your data source, you can use it to get all projects, a specific project, or any other GraphQL data you need on any page/template (page query) or even using static queries.
The idea should be similar to:
// gatsby-node.js
projects.forEach(({ node }, index) => {
createPage({
path: node.fields.slug,
component: path.resolve(`./src/templates/project.js`),
context: {
title: node.title,
},
})
})
In your gatsby-node.js (or File System Route API) you get all projects, loop through them and createPage for each project. The path (URL) for each project will be the slug field (node.fields.slug) but you can use whatever you want. Gatsby will create dynamic pages based on this field.
Then you decide which component will be used as a template: path.resolve(`./src/templates/project.js`) in this case and finally, you populate the context to add a unique value (title in this case: again, this can be an id, the slug, etc). This value will be used to filter the node in the template.
In your Project template:
export const query = graphql`
query ($title: String) {
mdx(title: {eq: $title}) {
id
title
}
}
`
In this case, I'm using markdown-based sources (that's why the mdx node) and this node is filtered by the title ($title) using the context value. The data will be inside props.data of the template. Again, if you want to fetch all projects you will have available an allMarkdown or allMdx (or allJSON...) depending on the source node)

ProviderFunction not exporting my new functions

My Problem :
I expect my FirebaseProvider function to provide an object containing all functions, through the app. The problem is that all functions are well provided through my files, except my last new function : fetchTest.
Explainations :
If I click the TestPage.js button I get Uncaught TypeError: fetchTest is not a function.
I saw many posts on stackoverflow about this type of error, but none did help me. -> I think the original problem is the index.js is not called. The console.log("firebaseprovider") (in index.js) does not appear in console, yet the other files of the project in web-app/src/views/ have the same imports and exports than TestPage.
Since App.js code worked fine on all the other files, I don't know how console.log("firebaseprovider") is never displayed in the navigator console. (edit: no matter which page I go, this console.log never appears)
<FirebaseProvider> seems to not provide TestPage.js.
Do you have an idea ?
What I've tried :
placing a console.log in TestPage.js : it shows every function written in index.js but not fetchTest. It seems to not be properly exported through api object.
in TestPage.js trying console.log("api.fetchTest") : console displays undefined.
add a second testing function in index.js, whithout parameters, which just does console.log("test")
compare imports/exports and api declarations with other files in web-app/src/views/
create a handleSubmit() function in TestPage.js to not put the functions directly in return
delete node_modules and then yarn install
yarn workspace web-app build and then relaunch yarn workspace web-app start
(This is a Yarn Workspaces project containing a common/ and a web-app/ folders)
common/src/index.js:
import React, { createContext } from 'react';
import {FirebaseConfig} from 'config';
const FirebaseContext = createContext(null);
const FirebaseProvider = ({ children }) => {
console.log("firebaseprovider"); // is not displayed in the console
let firebase = { app: null, database: null, auth: null, storage:null }
if (!app.apps.length) { // I tried to comment out this line (and the '}') -> no difference
app.initializeApp(FirebaseConfig); // no difference when commented out
firebase = {
app: app,
database: app.database(),
auth: app.auth(),
storage: app.storage(),
// [ ... ] other lines of similar code
api : { // here are functions to import
fetchUser: () => (dispatch) => fetchUser()(dispatch)(firebase),
addProfile: (details) => (dispatch) => addProfile(userDetails)(dispatch)(firebase),
// [ ... ] other functions, properly exported and working in other files
// My function :
fetchTest: (testData) => (dispatch) => fetchTest(testData)(dispatch)(firebase),
}
}
}
return (
<FirebaseContext.Provider value={firebase}>
{children}
</FirebaseContext.Provider>
)
}
export { FirebaseContext, FirebaseProvider, store }
web-app/src/views/TestPage.js:
import React, { useContext } from "react";
import { useDispatch } from "react-redux";
import { FirebaseContext } from "common";
const TestPage.js = () => {
const { api } = useContext(FirebaseContext);
console.log(api); // Displays all functions in api object, but not fetchTest
const { fetchTest } = api;
const dispatch = useDispatch();
const testData = { validation: "pending" };
return <button onClick={ () => {
dispatch(fetchTest(testData)); // Tried with/without dispatch
alert("done");
}}>Test button</button>
}
export default TestPage;
web-app/src/App.js:
import React from 'react';
import { Router, Route, Switch } from 'react-router-dom';
// ... import all pages
import { Provider } from 'react-redux';
import TestPage from './views/CreateSiteNeed'; // written same way for the other pages
import { store, FirebaseProvider } from 'common';
function App() {
return (
<Provider store={store}>
<FirebaseProvider>
<AuthLoading>
<Router history={hist}>
<Switch>
<ProtectedRoute exact component={MyProfile} path="/profile" />
<!-- [ ... ] more <ProtectedRoute /> lines, form imported Pages line 3. -->
<ProtectedRoute exact component={TestPage} path="/testpage" />
</Switch>
</Router>
</AuthLoading>
</FirebaseProvider>
</Provider>
);
}
export default App;
I hope some people will find this post helpful, thanks
Here was the problem :
Firstly :
I'm using Redux, so fetchTest() has its testActions.js and testReducer.js files, which are functionnal. But I did forget to update my store.js :
// [ ... ] import all reducers
import { testReducer as testData } from '../reducers/testReducer'; // was'nt imported
const reducers = combineReducers({
auth,
usersdata,
// [ ... ] other imported reducers
testData // My test reducer
}
// The rest is a classic store.js code
Secondly :
As I'm using Yarn Workspaces, I had to compile the code in common/dist/index.js to make it accessible through the whole entire code (even for local testing).
Here is the command to compile the code (-> to include all redux edits made above) and make it accessible to web-app workspace :
yarn workspace common build && yarn workspace web-app add common#1.0.0 --force
Explanations on the second part of the command (yarn workspace web-app add common#1.0.0 --force) :
The web-app/package.json file contains { "dependencies": { ... "common":"1.0.0" ... }}

How to solve import error for created component in ReactJs

I want to use a Html template at my Sharetribe Flex website which is build in ReactJS. I have added HomePage container, but it following error :
./src/routeConfiguration.js
Attempted import error: 'HomePage' is not exported from './containers'.
What I have tried:
I had added following code in routeConfiguration.js
const routeConfiguration = () => { return [
{
path: '/',
name: 'HomePage',
component: props => <HomePage {...props} />,
},
Pl. help me to solve this issue.
As you're exporting HomePage as default, the import in routeConfiguration.js should be import HomePage from 'containers'; - it's a common typo to put {} around the imported component and I'm guessing you may have done the same.
If not, then you need to check your env config files (babel, typescript, etc).
There are pages exported from src/containers/index.js file.
When JavaScript module bundler sees a module import with syntax like:
import { ComponentA, ComponentB } from '../containers'
it will assume that there is a file "../containers.js" or alternatively "../containers/index.js". In this case, the latter is true.
You should check Flex tutorials:
https://www.sharetribe.com/docs/tutorial-branding/add-faq-page/

Gatsby language localization Mdx files with gatsby-plugin-intl

I am trying to localize pages with gatsby-plugin-intl. No problem for .js files as I described below but how should I make it for .mdx files?
import React from "react"
import { FormattedMessage, injectIntl, navigate } from "gatsby-plugin-intl"
const MyPage = ( {intl} ) => (
<Layout>
<h2><FormattedMessage id="TITLE"/></h2>
</Layout>
)
export default injectIntl(MyPage)
I am trying to make the similar logic here:
---
name: Settings
route: /documentation/settings
---
import { Playground, Props } from 'docz'
import { useIntl, Link, FormattedMessage } from "gatsby-plugin-intl"
const intl = useIntl()
## Settings
intl.formatMessage({ id: "TITLE"})
every think look right but you have an error on the mdx file
const intl = useIntl()
use the FormattedMessage components instead message
example
<FormattedMessage id="TITLE" />
or your components
<Yourcomponents title={useIntl().formatMessage({ id: "TITLE" })} />
don't use const

error in displaying pdf in react-pdf

I am new new to react, I am trying to display a pdf file on browser. I am getting an error as failed to load PDF. I am trying to run the sample program given in https://www.npmjs.com/package/react-pdf.
App.js
import React, { Component } from 'react';
import { Document, Page } from 'react-pdf';
class MyApp extends Component {
state = {
numPages: null,
pageNumber: 1,
}
onDocumentLoad = ({ numPages }) => {
this.setState({ numPages });
}
render() {
const { pageNumber, numPages } = this.state;
return (
<div>
<Document
file="./1.pdf"
onLoadSuccess={this.onDocumentLoad}
>
<Page pageNumber={pageNumber} />
</Document>
</div>
);
}
}
export default MyApp;
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
ReactDOM.render(<App />, document.getElementById('root'));
registerServiceWorker();
Error screenshot
Question is old but hope this help someone. I faced this issue and found a solution here https://github.com/wojtekmaj/react-pdf/issues/321.
import { Document, Page, pdfjs } from 'react-pdf';
Add this to your constructor.
constructor(props){
super(props);
pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.js`;
}
You load the file using file="./1.pdf" I believe that might be the problem.
If you have a file structure like:
src
App.js
components
ShowPdfComponent.js
1.pdf
public
bundle.js
Then you need to move the 1.pdf to public folder like this:
src
App.js
components
ShowPdfComponent.js
public
bundle.js
1.pdf
Because when your compiled javascript code is being executed from public/bundle.js and bundle.js does not know how to get to src/components/1.pdf in file system.
There might be also a difference between production/development environment if you are using webpack and webpack-dev-server.
Look at react-pdf example. It has flat file structure. That is the reason why it works.
if you are using create-react-app then you need to import differently like
import { Document, Page } from 'react-pdf/dist/esm/entry.webpack'
because it uses webpack under the hood.
You can import the pdf file using import samplePdf from './1.pdf' and can use directly like file={samplePdf} in your Document tag.
Here is the minimum setup you need to be able to display your pdf in react TypeScript:
import { Document, Page, pdfjs } from 'react-pdf'
pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist#${pdfjs.version}/legacy/build/pdf.worker.min.js`;
const YourComponentReact = ({url}: {url: string}) => {
return (<Document file={url}>
<Page pageNumber={1} />
</Document>)
}
You can add a quick button if you want to be able to change pages
You only need to download react-pdf and #types/react-pdf if you use typescript.
adding pdfjs to import and using it like this hellped
import { Document, Page, pdfjs } from 'react-pdf'
pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist#${pdfjs.version}/legacy/build/pdf.worker.min.js`;
in the same file i need to show my pdf

Resources