I have a react application that generates HTML output based on some configuration. Like this:
export const getHtml = (config) => {
const {classes, children} = config
return (<div className={classes.join(' ')}>{children}</div>);
}
Inside the react app I can easily display the resulting DOM objects, but I want to save the HTML code to DB, to display it on a different page (without loading react/parsing the config again)
I could not find a way to convert the JSX object to plain HTML...
Use the renderToStaticMarkup method. As per the documentation:
You can use this method to generate HTML on the server and send the markup down on the initial request for faster page loads and to allow search engines to crawl your pages for SEO purposes.
const htmlString = ReactDOMServer.renderToStaticMarkup(
<div>
...
</div>
);
redux code:
Here is the full code that I had to use in my react/redux application.
import React from 'react';
import ReactDOMServer from 'react-dom/server';
import {Provider} from 'react-redux';
...
class ParentComponent extends React.Component {
...
getHtml(config) {
const {classes, children} = config
return ReactDOMServer.renderToStaticMarkup(
<Provider store={this.context.store}>
<ChildComponent classes={classes}>{children}</ChildComponent>
</Provider>
)
}
}
ParentComponent.contextTypes = { store: React.PropTypes.object };
Notes:
Use ReactDOMServer.renderToStaticMarkup() to get the HTML code
Specify ParentComponent.contextTypes to make this.context.store available
Need to wrap the ChildComponent in a Provider so it has access to the redux store.
Related
Trying next with layout pattern:
https://github.com/zeit/next.js/tree/canary/examples/layout-component
And the problem is that Layout component get remounted on every page change. I need to use layout component as a Container so it'll fetch data from server on every mount. How can I prevent layout to get re-mounted? Or am I missing something there?
This helped me for persistent layouts. The author puts together a function that wraps your page components in your Layout component and then passes that fetch function to your _app.js. This way the _app.js is actually the components that renders the Layout but you get to specify which pages use which layout (in case you have multiple layouts).
So you have the flexibility of having multiple layouts throughout your site but those pages that share the same layout will actually share the same layout component and it will not have to be remounted on navigation.
Here is the link to the full article
Persistent Layout Patterns in Next.js
Here are the important code snippets. A page and then _app.js
// /pages/account-settings/basic-information.js
import SiteLayout from '../../components/SiteLayout'
import AccountSettingsLayout from '../../components/AccountSettingsLayout'
const AccountSettingsBasicInformation = () => (
<div>{/* ... */}</div>
)
AccountSettingsBasicInformation.getLayout = page => (
<SiteLayout>
<AccountSettingsLayout>{page}</AccountSettingsLayout>
</SiteLayout>
)
export default AccountSettingsBasicInformation
// /pages/_app.js
import React from 'react'
import App from 'next/app'
class MyApp extends App {
render() {
const { Component, pageProps, router } = this.props
const getLayout = Component.getLayout || (page => page)
return getLayout(<Component {...pageProps}></Component>)
}
}
export default MyApp
If you put your Layout component inside page component it will be re-remounted on page navigation (page switch).
You can wrap your page component with your Layout component inside _app.js, it should prevent it from re-mounting.
Something like this:
// _app.js
import Layout from '../components/Layout';
class MyApp extends App {
static async getInitialProps(appContext) {
const appProps = await App.getInitialProps(appContext);
return {
...appProps,
};
}
render() {
const { Component, pageProps } = this.props;
return (
<Layout>
<Component {...pageProps} />
<Layout />
);
}
}
export default MyApp;
Also, make sure you replace all the to <Link href=""></Link>, notice that only have change the Html tag to link.
I struggled because with this for many days, although I was doing everything else correctly, these <a> tags were the culprit that was causing the _app.js remount on page change
Even though this is the topic Layout being mounted again and again, the root cause of this problem is that you have some data loaded in some child component which is getting fetched again and again.
After some fooling around, I found none of these problem is actually what Next.Js or SWR solves. The question, back to square one, is how to streamline a single copy of data to some child component.
Context
Use context as a example.
Config.js
import { createContext } from 'react'
export default createContext({})
_App.js
import Config from '../Config'
export default function App({ Component, pageProps }) {
return (
<Config.Provider value={{ user: { name: 'John' }}}>
<Component {...pageProps} />
</Config.Provider>
)
}
Avatar.js
import { useContext } from 'react'
import Config from '../Config'
function Avatar() {
const { user } = useContext(Config)
return (
<span>
{user.name}
</span>
)
}
export default Avatar
No matter how you mount and dismount, you won't end up with re-render, as long as the _app doesn't.
Writable
The above example is only dealing with readable. If it's writable, you can try to pass a state into context. setUser will take care the set in consumer.
<Provider value={useState({})} />
const [user, setUser] = useContext(Config)
setUser is "cached" and won't be updated. So we can use this function to reset the user anytime in child consumer.
There're other ways, ex. React Recoil. But more or less you are dealing with a state management system to send a copy (either value or function) to somewhere else without touching other nodes. I'll leave this as an answer, since even we solved Layout issue, this problem won't disappear. And if we solve this problem, we don't need to deal with Layout at all.
I am trying to speed up my application and in order to do this, I am lazy loading some components. One of my functional component loads from a .json file and it would be nice to lazy load it as well, but I am not sure how to do it. My code currently looks like this:
import React from 'react';
import Presentation from "../Presentation";
const data = () => import("../assets/sample.json");
const Finder = (props) => {
const {prop1} = props;
return ( <Presentation data={data} selection={prop1} /> );
};
export default Presentation;
When trying to render the Presentation component, it fails with an error (data undefined). How could I lazy load the json file? Is it possible using the react-loadable library? Thanks!
I am working a reactjs file that uses the react-ace library. Currently my code looks like this
import React, { Component } from 'react';
import 'brace/mode/html';
import 'brace/theme/monokai';
import AceEditor from 'react-ace';
class AceHTML extends Component {
render () {
return (
<AceEditor
mode="html"
theme="monokai"
name="Sample"
showPrintMargin={false}
wrapEnabled={true}
value={this.state.value}
editorProps={{
$blockScrolling: true
}} />
);
}
}
However I am trying to figure out a way to make it more generic. So I could say something like <Ace mode="javascript" /> and then in the component would import brace/mode/javascript instead of brace/mode/html
So my question is: What is the best way to load a library instead of using import?
PS: The reason I specifically pointed out that I am using react is because I am using create-react-app to create the application.
import all assets you want to use and you will be able to make changes as you please.
If you don't want to import all assets initially, you can use dynamic imports and load required chunks when a user requests a different editor configuration:
async changeTheme(theme) {
await import("brace/theme/" + theme)
this.setState({ theme });
}
async changeMode(mode) {
await import("brace/mode/" + mode)
this.setState({ mode });
}
live demo:
https://stackblitz.com/edit/react-nzivmp?file=index.js (without dynamic imports since they don't work on stackblitz)
import React from 'react';
import { render } from 'react-dom';
import brace from 'brace';
import AceEditor from 'react-ace';
import 'brace/mode/html';
import 'brace/mode/javascript';
import 'brace/theme/monokai';
import 'brace/theme/github';
function onChange(newValue) {
console.log('change',newValue);
}
// Render editor
export default ({mode, theme}) => (
<AceEditor
mode={mode}
theme={theme}
onChange={onChange}
name="UNIQUE_ID_OF_DIV"
editorProps={{$blockScrolling: true}}
/>
);
Importing libs isn't job for React. Webpack decides what to load to a bundle file. If you want to use any options based on props you'll need to import both anyway.
If there are large files and you don't want to load both of them for your application's user you can fetch them via AJAX request.
This one is piece of cake in React. If you want your MobX store to be available in any React component, you just use mobx-react #inject component. Something like:
import React from 'react';
import {inject} from 'mobx-react';
#inject('myStore')
class Dummy extends React.Component {
Then, my store is available as a prop:
this.props.myStore.myMethod();
Nice, convenient... and React only. Maybe I'm missing something, but I can't find a way to access my store from a plain ES6 class. How do I get the same result in a plain ES6 class in pure Vanilla Javascript?
Answer found in MobX GitHub account by adonis-work. Quoting the answer:
Exporting store as a singleton:
// Store.js
import { observable, computed } from 'mobx'
class Store {
#observable numClicks = 0
#computed get oddOrEven() {
return this.numClicks % 2 === 0 ? 'even' : 'odd'
}
}
const store = new Store()
export default store
... enables us to import and use the active store anywhere in the app as many times we want.
// Component.jsx
import store from Store
// use the global store in any component without passing it
// down through the chain of props from the root node
store.numClicks = 4
console.log(store.oddOrEven)
I am using this approach without problems for some time now. Are there any caveats I should look out for?
Link to source: https://github.com/mobxjs/mobx/issues/605
You forgot your code starts with:
import { Store } from "./store";
import { Provider } from "mobx-react";
import * as React from "react";
import { render } from "react-dom";
var store = new Store();
render(
<Provider {...stores}>
<Component />
</Provider>,
document.getElementById('root'),
);
Here you have your store variable and you can use it anywhere
If you'd like it even more convenient turn your store into a Singleton
than you can just import it anywhere like:
import { Store, instance } from "./store";
//in store.ts/js
export Store... =
export const instance = new Store(); // your singleton
how it works
the <Provider/> react component puts the Store in its react Context, read here more: https://reactjs.org/docs/context.html.
this means the store is in every child react component of <Provider/>.
Inject just simply copies this: this.props.store = this.context.mobx.store.
Thus making a singleton and using this singleton in your 'plain' javascript class (no react subclass/component), is the same thing.
Complete newbie to React and trying to find out how to load the HTML for components rather than inserting it directly in the render method. For example
import React, {Component} from 'react';
export default class Login extends Component {
render() {
return (
<!-- how can I provide a link to the HTML template for here? -->
);
}
}
React does not have HTML. The JSX that you write in the render method is actually compiled into JavaScript. At the core, React components are all JavaScript. The styles are also inline. Componentisation is neat in React because HTML, CSS, JavaScript (interactions) are all in one place, as JavaScript.
To insert raw HTML, React has an attribute dangerouslySetInnerHTML.
<div dangerouslySetInnerHTML={createMarkup()} />
If you want some type of organization, you can use variables to set the html and then assign that to the render functionalty, but React does not use html templates
var hello = React.createClass({
render: yourVariable
});
I think you might be confused about how JSX works.Just in case, I want to clarify that JSX is what they call "syntactic sugar", that turns React methods such as React.createElement into that XML like syntax. For instance:
var Nav;
// Input (JSX):
var app = <Nav color="blue" />;
// Output (JS):
var app = React.createElement(Nav, {color:"blue"});
(from the React docs)
So, to the best of my knowledge, the JSX syntax actually belongs in the render method. If what you are really looking for is the best way to separate and reuse purely presentational code, you should read on separating your app into Container and Presentational components
https://medium.com/#learnreact/container-components-c0e67432e005#.dzjqc8yrn
https://medium.com/#dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0#.mn9nf6lz6
Stateless/Presentational/Dumb Components are just functions that return JSX. So you could have your Template component:
import React from 'react';
//you need to import React to use JSX, as it will be expanded into React.createElement calls...
function Template(props){
return (
//...your template here...
);
}
And then your class
import React, {Component} from 'react';
export default class Login extends Component {
render() {
return (<Template {...props}/>);
}
}
}
Makes sense?