Clientside react-script overrides serverside rendered props - javascript

In my serverside react rendering, I pass a property to the JSX:
markup: React.renderToString(Admin({ field1: "Hallo" }))
The JSX looks like this:
<MaterialTextField hintText="field1" floatingLabelText="field1" type="text" name="field1" value={this.props.field1} />
Now, I ned to render the JSX also on clientside for having the event listeners, etc.:
React.render(
<Admin />,
document.getElementById('react-app')
);
The problem is: Because the rendered markups are not the same, the value of the text-field gets lost. How could I fix that?

React will check that any initial markup present matches what's produced for the first render on the client by comparing checksums between the initial client render and a checksum attribute in the server-rendered markup, so you must make the same props available for the initial render on the client in order to reuse the markup.
A common way to do this is to serialise the props to JSON so they can easily be included as a variable in the initial HTML sent to the client:
res.render('react.jade', {
markup: React.renderToString(React.createElement(MyComponent, props)),
props: JSON.stringify(props)
})
...
body
div#app
!= markup
script window.INITIAL_PROPS = !{props}
script(src='/js/app.js')

By passing it as prop to the Admin component just as you pass this.props.field1 to the MaterialTextfield using <Admin field1="Hallo" />

Related

Vue v-if with laravel backend data

I have an app which uses laravel for backend/api and vue components in views for frontend, this is a nice combo imo.
This is what I do right now:
#if(count($latestPosts) > 0)
<div class="LAYOUTwrapper_section">
<layout-title-2 :title="'Últimas publicaciones'"></layout-title-2>
<post-list-1 :posts="{{ json_encode( $latestPosts ) }}" :title="'Últimas publicaciones'"></post-list-1>
</div>
#endif
Now when I want to check if I should render a component (postsarray may be empty, then not render post component) I use blade conditional syntax, now this works but I'm thinking about moving this logic to vue components, keep blade syntax to minimum and handle this in the frontend with vue entirely.
Now my question is how can I check if a laravel array is empty in vue component placed on blade view file, not inside the component, basically I want to use v-if to check if $posts ilength is more than 0 inside v-if
Bellow code doent't work but I'd like something similar.
<div class="LAYOUTwrapper_section" v-if="{{ json_encode($latestPosts).length > 0 }}>
<layout-title-2 :title="'Últimas publicaciones'"></layout-title-2>
<post-list-1 :posts="{{ json_encode( $latestPosts ) }}" :title="'Últimas publicaciones'"></post-list-1>
</div>
You have a few options.
Better Solution
Really decouple your component by moving the data behind an API and have your component request the information it needs from an endpoint. Your App/Wrapper/Component should be responsible of getting the data it needs.
Not so great solution
v-if expects a Javascript conditional, so you could just get your blade to render the HTML template as v-if="true" or v-if="false", Note that if you mistakenly pass "false" instead of false, Javascript will interpret this as a truthy value, and the logic will break.
When vue takes over and renders the component, the boolean should kick in on mounted.
You can also pass the value as a prop to your component.
<layout-wrapper :shouldDisplay="{{..}}" /> and use that on the v-if.
{
template: `
<section/>
<div class="LAYOUTwrapper_section" v-if="shouldDisplay"></div>
</section>
`,
props: {
shouldDisplay: Boolean
}
}

Does React Router necessarily complicate state management (without Redux)?

I'm fairly new to React to trying to wrap my head around routing via React Router while also passing required data to components. I will probably eventually incorporate Redux in my app, but I'm trying to avoid it initially.
It seems like using React Router as opposed to serving individual pages from the server means having to store state data in the App.js component since that's where the Router exists.
For example if I'm on site.com/x and I want to navigate to site.com/y and /x looks like this:
<div>
<XOuter >
<XInner />
</XOuter>
</div>
And App.js looks like this:
<BrowserRouter>
<Route exact path="/x" component={X} />
<Route exact path="/y" component={Y} />
</BrowserRouter>
... if the GET request is being called from XInner and the results will inform the content of /y, XInner will have to pass the response all the way back to App.js to properly render /y.
It seems like this could get messy quickly. Is there any way to avoid it?
This isn't as bad as you think, for two reasons:
If you use React Router's <Link> component to create links instead of using <a> directly, it will add event handlers that cancel the link's actual navigation and instead use history.pushState to do the navigation. To the user, they think they're on the new page (the URL bar shows this), but no GET request ever actually happened to load it.
React Router's paths are parsed via path-to-regexp. This lets you add parameters to the URL and then extract them from the router's props. You can also put data in the query string and then parse it later. This will let you pass state from one page to another without using any top-level React state, with the added benefit of making the browser's history and URL copying automatically work right.
The data is stored in the path instead of App.js. Path should be converted to props through pure function so the same path is always converted to the same props. That's the external state that chooses a <Route /> and sets its props.
Root of your problems lies in this design:
if the GET request is being called from XInner and the results will inform the content of /y, XInner will have to pass the response all the way back to App.js to properly render /y
Remove the if the GET request is being called from XInner... and all your concerns become moot.
Component A should not be responsible for fetching data for Component B. If /y needs data, fetch the data in Y's componentdidmount.
Example code showing the concept
fetchData(){
fetch(...) // or axios or whatever
.then(() => {
this.setState({
data: 'Hello World'
})
})
}
componentDidMount(){
this.fetchData()
}
render() {
return(
<div>
{this.state.data}
</div>
)
}

Vue.js - component inside component

I do have my component called Grid. Inside this component I load JSON data from server and i render them. They are mostly string and integers. Sometimes the JSON contains HTML like <strong>myvalue</stong> so I render the data with three brackets {{{ and }}}.
The thing is when the HTML is not pure HTML but component like <my-component my-param="1"></my-component>. How to tell to Vue.js to render this coponent? All I get is the HTML purely printed into grid.
Thanks
You need to compile again that piece of code you've loaded from remote.
ps: I will use jQuery to manipulate the DOM.
Inside this component I load JSON data from server and i render them.
I'll assume you have a function named "loadAndRenderFromServer()", please adapt the code below to fits you.
Eg: If your grid has the markup <div id='grid'>
// vuecomponent.js
export default {
[...]
methods: {
loadAndRenderFromServer() {
// first, load remote content and insert into #grid
// now, compile
this.$compile($("#grid").get(0));
}
},
[...]
}
You may need to use "decompile" if your component starts to duplicate the loaded content. Check into VueJS docs for compile and decompile methods.
Using v-html (which is equivalent to {{{}}}) will not render what's inside it if it's a component.
Instead try to use <slot> in your parent template.
Otherwise, if you want dynamic components you need to use <component> and if you want content inside those dynamic component you need to use <slot>s.
I would suggest you to use something like
<component :is="myComponent" />
and inside the models of those components put some <slot>s to insert arbitrary content.

Render multiple React components into a single DOM element

I'm looking to render multiple modals into a single ReactDOM element. Here's the HTML structure that React renders to.
<body>
<div id="modal-socket"></div> // Insert multiple here
<div id="wrapper">
// Other content goes here
</div>
</body>
There's a long story behind why I need to render multiple components into #modal-socket but I want to do something akin to this:
ReactDOM.render(<AddMeasurableModal />, document.getElementById("modal-socket"));
ReactDOM.render(<AddMeasurableModal />, document.getElementById("modal-socket"));
ReactDOM.render(<AddMeasurableModal />, document.getElementById("modal-socket"));
Obviously this replaces the current content of #modal-socket on each render call.. So I don't get my end result. Boo.
Did a search and found a few answers on it but none meet my needs.
Cheers.
As you told in a comment, the dynamic way would be something like this
Inside of a main component you could do:
Imagine having an array like:
let myArray = [
{
prop1: 'hello world'
},
{
prop1: 'Hey there!'
}
]
//Then in the render function (you can put that array into the state or something)
render(){
return (
<div>
{myArray.map((entry,index) => {
return <AddMeasurableModal key={index} {...entry} />
})}
</div>
)
}
this will create as many AddMeasurableModal components as there are entrys in the myArray variable and add every property stored as props onto the component (In this case, every AddMeasurableModal component has access to the this.props.prop1 value, because of the {...entry} spread syntax)
Notice how I only put myArray.map() into the render function inside of {}?
React renders every array of components without further configuration inside of the render function. And Array.map() returns an array. Just make sure to return only valid react elements! When doing this, don't forget to add a uniqe key prop to each element to avoid warnings.
EDIT: in this case, the key prop is the current index in the array, but when fetching data from a server I would recommend to use a uniqe id from the database or something to avoid rendering bugs.
If you don't want to map over an array, you can just set a number of components and then loop over them, creating an array of components and put them into the render function.
Wrap your multiple modals into 1 container and render that, eg:
let modals = (
<div>
<AddMeasurableModal />
<AddMeasurableModal />
<AddMeasurableModal />
</div>
);
ReactDOM.render(modals, document.getElementById("modal-socket"));

How to handle child components with isomorphic React?

I have server code like this:
var data = {
scripts: scripts,
children:[<Comp1 />, <Comp2 />, <Comp3 />]
};
// keeping smaller for easier example than reality
var markup = '';
markup += '<script type="text/javascript">' +
'var PROPS = {' +
'Layout: ' + JSON.stringify(data) +
'};' +
'</script>';
markup += React.renderToString(
<Layout {...data} ></Layout>
);
So the server renders everything fine. Then no matter what I try, I get all sorts of warnings about how I'm handling the children for serialization and re-use in the browser when I run: React.render(App(window.PROPS.Layout), document.getElementById('content'));
Many of my attempts make React complain that I should be using createFragment. But when I do that, I still get errors that I should be wrapping it.
My goal is to render the Layout component, with several children, and have React in the browser know the same elements are below. Many attempts also yield this error:
Warning: React attempted to reuse markup in a container but the checksum was invalid. This generally means that you are using server rendering and the markup generated on the server was not what the client was expecting. React injected new markup to compensate which works but you have lost many of the benefits of server rendering. Instead, figure out why the markup being generated is different on the client or server:
(client) d=".1frk89jhyio.1"></div><div data-react
(server) d=".1frk89jhyio.1"><div style="float:lef
My client code is this:
var React = require('react'),
Layout = require('./components/layout');
React.render(<Layout {...PROPS.Layout} />, document.getElementById('content'));
You shouldn't stringify your components like that to serve for your client. You should just render you application again. Things to stringify should only be raw data like {id: 1, name: 'limelights'}.
On the server
React.renderToString(<Layout {...data} />);
and on the client
var data = JSON.parse(window.PROPS.data);
React.render(<Layout {...data} />, document.body);
The point of having an isomorphic application is that the same code runs on both the server and the client.
The answer is that I was doing things the theoretical wrong way.
Having the server render like <Layout {...data} ><Comp1 /></Layout> will work. However when the browser kicks in, it won't have children props and you'll lose Comp1. Then I had tried to pass children through in as props like the example in my question. The real way to do this is simply having the server do <Layout {...data} ></Layout>.
The real key to the answer is that the render method of Layout should be placing the children directly. I can use state (or props, still working out semantic differences) within render to determine if children should be different.

Categories