I'm currently in the process of learning React, so I've build my little game based on react components and I wanted to add translations for the various buttons needed.
The way we usually approach this is that the texts and translations are handled and edited by the client (as in the people we work for) on the server. Those translations would then be rendered right into the html template so that was it.
In React, however, everything is in js and component based. Does this mean that to make the move towards react we have to change our whole localization system, even on the server? Am I doing something wrong?
I tried having this
<script>
var translations = {
start: "Spiel starten"
}
</script>
<div id="App"></div> <!-- this is where I render React -->
but it looks like translations is undefined in my React App.js file
One option for handling this situation could be perhaps to pass in your translations object as a property on your component. For example:
<script>
var translations = {
start: "Spiel starten"
}
</script>
<div id="App"></div>
ReactDOM.render(
<Game translations={translations} />,
document.getElementById('App'));
As you have passed in your translations object into your react component, you should be able to use it in your template as such:
var Game = React.createClass({
render: function(){
return (
<div>
{this.props.translations.start}
</div>
);
}
});
Related
I have a GatsbyJS site that I am working on where the main content source is a Wordpress install. One of the things I like to add to my sites is the ability to have placeholder areas in the site where I can control the content via the CMS. Usually I have a header_scripts area that goes at the bottom of the <head> tag, a body_scripts area that goes at the start of the <body> tag, and a footer_scripts area that goes at the bottom of the page <body>. With these three, I can usually integrate third-party add-ins pretty easily without having to do code deployments.
Sometimes I need to embed stylesheets, sometimes I need to embed script tags, and sometimes I need to throw in <meta> tags. Really the content could be anything. This data comes back as a raw string from my Wordpress GraphQL endpoint.
So now my question is, how do I get this content injected into my Gatsby site in the following places:
<html>
<head>
...
{header_scripts}
</head>
<body>
{body_scripts}
...
{footer_scripts}
</body>
</html>
I've found so far that I can just include the body_scripts and footer_scripts in a fairly regular manner in my Gatsby page template. In gatsby-node.js, I pass in the property values using the pageContext. It's kind of a bummer that they need to be wrapped in a <div /> tag, but they seem to work just fine.
import React from 'react'
export default class PageTemplate extends React.Component {
render = () => {
return (
<React.Fragment>
{this.props.pageContext.bodyScripts && (
<div dangerouslySetInnerHTML={{__html:this.props.pageContext.bodyScripts}} />
)}
{/* my page content here */}
{this.props.pageContext.footerScripts && (
<div dangerouslySetInnerHTML={{__html:this.props.pageContext.footerScripts}} />
)}
</React.Fragment>
)
}
}
Now for the real question. I am stumped on how to get the dynamic content from the header_scripts into the Gatsby server-side-rendering <head> tag. The closest thing I have found to being able to inject content into the head is to leverage the gatsby-ssr.js onRenderBody function. However, this seems to require pre-determined React component instances in order to function. I can't just pass it in plain raw string content and see the output in the page source:
export const onRenderBody = async ({
pathname,
setHeadComponents,
setHtmlAttributes,
setBodyAttributes,
setPreBodyComponents,
setPostBodyComponents,
setBodyProps
}, pluginOptions) => {
setHeadComponents(['<script>alert("hello");</script>'])
}
This results in an escaped string getting inserted into the <head> tag:
<html>
<head>
...
<script>alert("hello");</script>
</head>
<body>
...
</body>
</html>
I'm at a loss as to how to proceed. I can't just wrap my string in a <div /> tag like in the body because div tags can't go inside the head tag. I can't think of any head-capable HTML tags that would accept this kind of content.
The only idea I've had is to actually parse the string content into full React components. This seems daunting given the number of possible tags & formatting that I would need to support.
Am I going about this the wrong way? How can I get my arbitrary content into my Gatsby site's head tag?
It's a broad question and it will need some trials and errors to ensure that it's fully working without caveats in all scenarios but, among the things you've tried, you can add a few more options to the list to check which ones fit better.
Regarding the body_scripts and footer_scripts both can be inserted using the:
<div dangerouslySetInnerHTML={{__html:this.props.pageContext.footerScripts}} />
In any desired page or template. For the header_scripts and the meta tags (SEO), you can use the <Helmet> component. Basically, using this component, everything that is wrapped inside, it's becomes transpiled inside the <head> tag once compiled.
export default class PageTemplate extends React.Component {
render = () => {
return (
<React.Fragment>
<Helmet>
{this.props.pageContext.headerScripts && (
<div dangerouslySetInnerHTML={{__html:this.props.pageContext.headScripts}} />
)}
</Helmet>
{this.props.pageContext.bodyScripts && (
<div dangerouslySetInnerHTML={{__html:this.props.pageContext.bodyScripts}} />
)}
{/* my page content here */}
{this.props.pageContext.footerScripts && (
<div dangerouslySetInnerHTML={{__html:this.props.pageContext.footerScripts}} />
)}
</React.Fragment>
)
}
}
However, if the data comes from a CMS, it won't be available in the SSR yet, so, one easy thing you can do is to customize the outputted HTML (html.js) that Gatsby generates in each compilation. From the docs:
Customizing html.js is a workaround solution for when the use of the
appropriate APIs is not available in gatsby-ssr.js. Consider using
onRenderBody or onPreRenderHTML instead of the method above. As a
further consideration, customizing html.js is not supported within a
Gatsby Theme. Use the API methods mentioned instead.
Run:
cp .cache/default-html.js src/html.js
Or manually, copy the .cache/default-html.js file and paste it /src folder. There you can customize the final HTML.
Just wondering what the procedure is loading an external script into a component which renders HTML?
I call a script, which returns me an unordered list which i want to use for an image carousel it looks something like this but currently isn't working
this is what i have inside my render
return(
<Slick {...settings}>
<script
type="text/javascript"
src="https://some.external.script.js"
/>
<div
class="render-html-here-from-script"
/>
<script>
SomeScript.require(['xxx'], function(arg)
{arg.loadAll().done(function(embed) {
// code here gets list and binds above in div
})}
)
</script>
</Slick>
)
the above code works fine if I just copy/paste straight into a standard HTML page
Thanks
I have a simple app created using CRA v2 that provides a "load more" button after lists of posts. The default posts displayed on the page are generated server-side based on a set of criteria (ie. specific post type, taxonomy terms, etc), and the "load more" button queries an API to display more posts that match the same criteria.
My pages will have an undefined (but >1) number of post lists on a page, and not all of the lists will be nearby each other, so the whole thing can't exist in a single app. I need to be able to render the app more than once per-page and have them operate independently.
Best case scenario, I'd be able to do something like this:
<ul class="posts posts--foo">[first list of posts from the "foo" post type go here]</ul>
<div id="app-root" data-post-type="foo"></div>
<ul class="posts posts--bar">[second list of posts from the "bar" post type go here]</ul>
<div id="app-root" data-post-type="bar"></div>
<script src="main.7a3cc682.js"></script> <!-- built script-->
I realize this won't work as written. Is this possible, and if so what's the best way to make this work?
I was able to find a solution using the answer to this question. Here's what it looks like:
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import render from 'react-dom';
import App from './App';
window.mount = function(id) {
let app = document.getElementById(id);
ReactDOM.render( <WPLoadMore {...(app.dataset)} />, document.getElementById(id) );
}
and then in my HTML:
<script src="build/static/js/main.7a3cc682.js"></script>
<ul class="posts posts--foo"></ul>
<div id="app1" data-post-type="foo"></div>
<script type="text/javascript">mount("app1");</script>
<ul class="posts posts--bar"></ul>
<div id="app2" data-post-type="bar"></div>
<script type="text/javascript">mount("app2");</script>
The only slightly wonky bit about this is that in the index.html in my public directory, I needed to move the mount() outside of the </body> tag so that it loads after all of the React scripts, like so:
</body>
<script type="text/javascript">mount("wplm");</script> <!-- included outside the body so this works in development -->
</html>
I use react 16. I want to render portal inside my app like that:
<html>
<body>
<div id="app-root">
<div>...My app stuff</div>
<div id="modal-root">... portal stuff</div> <-- portal content
</div>
</body>
</html>
But official doc recommends render portal next to, not in app.
<html>
<body>
<div id="app-root"></div>
<div id="modal-root"></div>
</body>
</html>
Is it the only correct way to use the portal?
The idea of portal is that you can render it anywhere in the DOM tree, all you need is a valid DOM Node to render it into, its not necessary for it to be next to app-root
According to the Docs
However, sometimes it’s useful to insert a child into a different
location in the DOM:
render() {
// React does *not* create a new div. It renders the children into `domNode`.
// domNode is any valid DOM node, regardless of its location in the DOM.
return ReactDOM.createPortal(
this.props.children,
domNode,
);
}
I have a question regarding React patterns. When rendering a component, is it appropriate for this component to render several sub-components, or is it ok to render some HTML in the parent component. Example:
If I have a box that has a heading and a body with list of elements, should I do:
var Box = React.createClass({
render: function() {
<div className="box">
<HeadingBox />
<BodyBox />
</div>
}
});
Or is it ok to do this:
var Box = React.createClass({
render: function() {
<div className="box">
<div className="heading">
<div> Heading1 </div>
<div> Heading2 </div>
</div>
<BodyBox />
</div>
}
});
Any rules to follow here?
It all depends on a context.
The general practice is that if you want to reuse the markup anywhere — you should go with the separate component, so you don't have to repeat yourself. Also if you find yourself writing a large portion of HTML (over 50 lines, for example), separating it into subcomponents will also help.
In other cases, just going with plain HTML will do.
You can find a good description on how best to organize your React code here. (section Separating UI details from interaction logic)
React is no different then other programming framework — it goes best with DRY (Don't repeat yourself).