How to map dynamic routes to components outside pages folder in a NextJs multi tenant application - node.js

I am following this template here to create a Multi-tenant application using NextJS.
However, I am stucked at how to properly resolve the routing of the pages.
My pages folder is structured in this manner
pages/
_sites/[site]
[path.jsx]
index.jsx
I have the routing logic inside[path.jsx] file above
I have moved all my components from the pages folder to another folder called components.
Now, when a user visits for example james.mydomain.com/blog I wish to load the blog component from the components folder.
How can that be neatly done without too much hardcoding?
Here is what I have attempted but the page only freezes without loading the component:
import { useRouter } from "next/router";
import { useEffect, useState } from "react";
import Loading from "react-loading";
export default function SiteComponent(props) {
const router = useRouter();
const [component, setComponent] = useState(null);
const { path } = router.query;
const loadComponent = async (path) => {
const importedComponent = await import(`../../../src/components/${path}`);
setComponent(importedComponent.default);
}
useEffect(() => {
if (path) {
loadComponent(path);
}
}, []);
return (
component ? <component /> : <Loading color="teal" type="bubble" />
);
}
Is there a way to do this neatly without the above dynamic component loading?
I feel the above code may not even work properly on the occasion I wish to load a nested route eg. james.mydomain.com/blog/categories.
Please kindly suggest a cleaner approach.

Related

Azure Static Web App routes case sensitive

I'm building a static site using nextjs and hosting it in azure using the static web app offering. After I deployed my app, I noticed that the dynamically generated url's from next export are actually case sensitive, i.e. https://MYAPP.azurestaticapps.net/MYPAGE does not resolve but https://MYAPP.azurestaticapps.net/mypage does resolve. Is there a way to solve this at either the nextjs or azure level?
Normally in nextjs, all URL resolution should be case insensitive, but there is currently a bug "URL resolution case sensitivity is inconsistent"
https://github.com/vercel/next.js/issues/21498
There is workaround to fix the issue - implement redirecting with a dynamic route:
import { useEffect } from 'react'
import { useRouter } from 'next/router'
import Error from 'next/error'
export default function ResolveRoute() {
const router = useRouter()
useEffect(() => {
if (router.pathname === router.pathname.toLowerCase()) {
// Route is already lowercase but matched catch all
// page not found, display 404
return <Error statusCode={404} />
} else {
router.push(router.pathname.toLowerCase())
}
})
return <p>Redirecting...</p>
}
Another option is to use Azure ApplicationGateway as its rules are case insensitive.
https://learn.microsoft.com/en-us/azure/application-gateway/url-route-overview

Unable to add an UI extension on Contentful. When i run nopm run start, I get a 404 error on ui_config

I am new to contentful. I am trying to develop an UI extension on Contentful using Contentful SDK.
I followed all the steps mentioned in this article.
This is the code I have in index.js.
import React from 'react';
import PropTypes from 'prop-types';
import ReactDOM from 'react-dom';
import { TextInput , Button } from '#contentful/forma-36-react-components';
import { init } from 'contentful-ui-extensions-sdk';
import '#contentful/forma-36-react-components/dist/styles.css';
import './index.css';
export class App extends React.Component {
static propTypes = {
sdk: PropTypes.object.isRequired
};
detachExternalChangeHandler = null;
constructor(props) {
super(props);
this.state = {
value: props.sdk.field.getValue() || ''
};
}
componentDidMount() {
this.props.sdk.window.startAutoResizer();
// Handler for external field value changes (e.g. when multiple authors are working on the same entry).
this.detachExternalChangeHandler = this.props.sdk.field.onValueChanged(this.onExternalChange);
}
componentWillUnmount() {
if (this.detachExternalChangeHandler) {
this.detachExternalChangeHandler();
}
}
onExternalChange = value => {
this.setState({ value });
};
onChange = e => {
const value = e.currentTarget.value;
this.setState({ value });
if (value) {
this.props.sdk.field.setValue(value);
} else {
this.props.sdk.field.removeValue();
}
};
onButtonClick = async () => {
console.log('hurray');
};
render() {
return (
<Button buttonType="primary" isFullWidth={false}
onClick={this.onButtonClick}>
Add Content from AEM DAM
</Button>
);
}
}
Ideally i am trying to create an UI extension to be used in contentful space. I downloaded the contentful SDK and i have put in a button. But I receive this error on the console and it doesn't work
Screenshot:
https://github.com/contentful/create-contentful-extension
Go to the content of this Content Type and enable mixed content at
your browser so that development version that is served from your
local machine could be rendered within https://app.contentful.com.
Better yet:
I'm not the biggest fan of disabling the mixed content setting in browsers. Can I use HTTPS in development mode?
Yes, you can serve your extension using HTTPS. Add --https to start command.
"start": "contentful-extension-scripts start --https",
It uses Parcel HTTPS under the hood , which generates a self-signed certificate, you might have to configure your browser to allow self-signed certificates for localhost.
I think that will fix the 404 error and get things working.
Please follow the readme carefully and post a separate question if you still have problems.

Dynamic file names in react native require()

We're working on an app that will allow users to store templates with images on them, and pull those templates up later. This is for an AR environment using Viro on React Native.
We're trying to dynamically load an image into the component, and receiving errors when we require the filepath, which has been set to a variable:
const exampleUri = '/some/uri'
render() {
return(
<Viro3DObject
source={require(exampleUri)}
/>)
}
The URI for the source prop has to be dynamic, as the URIs are pulled from a Database.
We've tried storing the entire request in the database (in models/element.js):
const Sequelize = require('sequelize');
const db = require('../db');
const Element = db.define('element', {
sourceViro3DObject: {
type: Sequelize.STRING
}
});
sourceViro3DObject: `require('../../assets/emoji_heart/emoji_heart.vrx')`
When we called it in the React Native class component:
getObjectData = async () => {
try {
const {data} = await axios.get(`/api/elements/${this.props.elementId}`)
this.setState({sourceViro3DObject: data.sourceViro3DObject})
} catch (err) {
console.log(err)
}
}
async componentDidMount() {
await this.getObjectData()
}
But this simply sets state.sourceViro3DObject to a string:
'require('../../assets/emoji_heart/emoji_heart.vrx')'
We've tried setting the filepath directly to state as a string:
state.sourceViro3DObject = '../../assets/emoji_heart/emoji_heart.vrx'
and then call require on it:
require(this.state.sourceViro3DObject)
and received the following error:
Invalid prop `source` supplied to `Viro3DObject`
We've seen recommendations of storing the URIs in an object, but that can't work for us as we don't know what image is going to be used until it's pulled from the database. We can't hard-code them anywhere.
We'd appreciate any help with this!

Testing React Router with Jest and Enzyme

My goal is to test my React Router Route exports in my app and test if it is loading the correct component, page, etc.
I have a routes.js file that looks like:
import React from 'react';
import { Route, IndexRoute } from 'react-router';
import { App, Home, Create } from 'pages';
export default (
<Route path="/" component="isAuthenticated(App)">
<IndexRoute component={Home} />
<Route path="create" component={Create} />
{/* ... */}
</Route>
);
Note: isAuthenticated(App) is defined elsewhere, and omitted.
And from what I've read, and understood, I can test it as such:
import React from 'react';
import { shallow } from 'enzyme';
import { Route } from 'react-router';
import { App, Home } from 'pages';
import Routes from './routes';
describe('Routes', () => {
it('resolves the index route correctly', () => {
const wrapper = shallow(Routes);
const pathMap = wrapper.find(Route).reduce((pathMapp, route) => {
const routeProps = route.props();
pathMapp[routeProps.path] = routeProps.component;
return pathMapp;
}, {});
expect(pathMap['/']).toBe(Home);
});
});
However, running this test results in:
Invariant Violation: <Route> elements are for router configuration only and should not be rendered
I think I understand that the issue might be my use of Enzyme's shallow method. I took this solutions from this SO question. I believe I understand that it is attempting to parse through the wrapper in search of a Route call, putting each into a hashtable and using that to determine if the correct component is in the table where it should be, but this is not working.
I've looked through a lot of documentation, Q&A, and blog posts trying to find "the right way" to test my routes, but I don't feel I'm getting anywhere. Am I way off base in my approach?
React: 15.4.2
React Router: 3.0.2
Enzyme: 2.7.1
Node: 6.11.0
You can't directly render Routes, but a component with Router that uses routes inside. The answer you pointed to has the complete solution.
You will probably also need to change the browserHistory under test environment to use a different history that works on node. Old v3 docs:
https://github.com/ReactTraining/react-router/blob/v3/docs/guides/Histories.md
As a side note, what's the point of testing Route which I assume is already tested in the library itself? Perhaps your test should focus on your route components: do they render what they should based on route params? Those params you can easily mock in your tests because they're just props.
I'm telling you this because in my experience understanding what to test was as important as learning how to test. Hope it helps :)

Can I do the server rendering with data in React?

I'm working on react recently. I am using the server rendering with react-router, but it can only render the HTML from react components, and I need to get the initial data via ajax in componentDidMount().
The problem is for some pages/components, they need the initial data to render. So if users visit such a page directly(by typing url or refresh), the page broken, because the server cannot render it without initial data.
I'm thinking can I get the data from database and insert it to template when rendering in server? Just like what classic front-end template or PHP does.
If no way, what's the best practice for rendering first-page data?
Now my server code is like:
router.get('*', function(req, res) {
console.log('GET ADMIN');
match({
routes: appRoutes,
location: req.originalUrl
}, (error, redirectLocation, renderProps) => {
if (error) {
res.status(500)
.send(error.message);
} else if (redirectLocation) {
res.status(302)
.redirect(redirectLocation.pathname + redirectLocation.search);
} else if (renderProps) {
var content = renderToString(<RoutingContext {...renderProps}/>);
var html = swig.renderFile('src/client/admin.html', {
content: content
});
res.status(200)
.send(html);
} else {
res.status(404)
.send('Not found');
}
});
});
Finally I found the answer in GitHub: https://github.com/rackt/react-router/issues/1969#issuecomment-140988110
This method insert all data you need into the HTML template when rendering in server and get the data in front-end as context. All component can get the data via context.
I use a <DataWrapper> component to wrap the original root app component. On the server side when rendering:
data = JSON.stringify(data); // data is an object
ReactDOMServer.renderToString(<DataWrapper data={data}><RoutingContext {...renderProps}/></DataWrapper>)
and the <DataWrapper> is something like:
import React from 'react';
class DataWrapper extends React.Component {
getChildContext () {
return {
data: this.props.data
};
}
render () {
return this.props.children;
}
}
DataWrapper.childContextTypes = {
data: React.PropTypes.object.isRequired
};
export default DataWrapper;
on the client side:
var data = JSON.parse(document.getElementById('data').innerHTML),
history = createBrowserHistory();
ReactDOM.render(<DataWrapper data={ data }><Router history={ history }>{ routes }</Router></DataWrapper>, document.getElementById('app'));
get the data via context in components:
class someComponent extends React.Component {
constructor (props, context) {
super(props, context);
this.state = context.data.someData;
}
}
someComponent.contextTypes = {
data: React.PropTypes.object.isRequired
};
export default someComponent;
it works well
The problem with using an AJAX call for the initialisation is that it forces you to render first without the data and then to re-render once the data arrives.
A better way is to include the data in the page so it's there for your initial render. e.g.
<script>
var __INITIAL_DATA__ = { /* etc */ }
</script>
Yeah globals are ugly. But necessary in this instance. And you only use it once to initialise your React component. (Or your store if you're using Flux.)
And yes indeed, in isomorphic/universal apps you use the same data in your server side code. Just like in PHP and other server side languages. Only your server side code won't be pulling from a global variable. The data should be passed directly into the React component that you're mounting.

Resources