Use Link in React Component Rendered on Server - javascript

I am using pay pals react engine. I have my layout component which calls my Nav component. Since Nav is global I wanted to put it within Layout, both seem to be rendered server side. The problem comes when I want to use react routers Link function in Nav.
Layout:
var React = require('react');
var Nav = require('../../server/components/Nav.jsx');
module.exports = React.createClass({
componentDidMount: function(){
try{Typekit.load({ async: true });}catch(e){}
},
render: function render() {
return (
<html>
<head>
<meta charSet='utf-8' />
<link rel="stylesheet" type="text/css" href="/css/main.css" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>
{this.props.title}
</title>
</head>
<body>
{ /*trying to pass router as a prop as a useless attempt to make this work*/ }
<Nav router={this.props.router} />
<div className="wrap">
{this.props.children}
</div>
</body>
<script src='/bundle.js'></script>
</html>
);
}
});
Here is my Nav component. You can see within the UL tag I am using anchor elements. I would like the use the tag so react router will do its thing rather than having to send a new request to the server. I've tried a few different things to make it would however I've been unsuccessful. Is it even possible to use react router in a component rendered from the server?
var React = require('react');
var Logo = require('./logo.jsx');
var Nav = React.createClass({
render: function(){
var mobileMenuShow = function(e){
var el = e.currentTarget;
var list = document.querySelector('.main-nav-list');
if(el.innerHTML === '+'){
el.innerHTML = '-';
list.setAttribute('class', 'main-nav-list open');
} else{
el.innerHTML = '+';
list.setAttribute('class', 'main-nav-list');
}
};
return (
<nav className="background-first main-nav">
<section className="nav-wrap">
<section className="main-nav-left">
<div className="main-nav-logo"><Logo /></div>
<div className="plus-icon" onClick={mobileMenuShow}>+</div>
</section>
<section className="main-nav-right">
<ul ref="navList" className="main-nav-list">
<li>Sign Up</li>
<li>Log In</li>
<li>Quick Start</li>
<li>Docs</li>
<li>About</li>
</ul>
</section>
</section>
</nav>
)
}
});
module.exports = Nav;

Is it possible, yes. As long as you have a client router kick off after the page is rendered from the server. Basically, if the only router in on the server, then it will always handle the requests. But, if you have one loaded in the client too, then it will pick up the client transitions and only use the server when you do a page reload (F5) or enter a new URL. Check out the React Router Mega Demo to see how the client.js file is also creating a router after the app is loaded from the server.
To accomplish the Link / <li> issue, you just need to create a component to return the link inside of a tab.
Here's an example of how to accomplish this (taken from issue 666).
var NavTab = React.createClass({
contextTypes: {
router: router: React.PropTypes.func
},
render: function () {
var isActive = this.context.router.isActive(this.props.to, this.props.params, this.props.query);
var className = isActive ? 'active' : '';
var link = (
<Link {...this.props} />
);
return <li className={className}>{link}</li>;
}
});

Related

How to import js file from url and initialize a class in React?

I want to embed PitchPrint app on a React website. They have a vanilla html/js integration tutorial here. I added script tags with links to jQuery and their app file in my index.html file, as they require and then created a separate jsx file that suposed to return a button witch opens the app. The problem is, when I try to build, it throws an error 'PitchPrintClient' is not defined witch suposed to come from their files.
My index.html file:
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
<script src="https://pitchprint.io/rsc/js/client.js"></script>
<title>App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>
My jsx file:
import React from 'react';
const AppButton = () => {
let _launchButton = document.getElementById('launch_btn');
let _previewDiv = document.getElementById('pp_preview_div');
let _loaderDiv = document.getElementById('pp_loader_div');
_launchButton.setAttribute('disabled', 'disabled');
var ppclient = new PitchPrintClient({
apiKey: 'f80b84b4eb5cc81a140cb90f52e824f6', //Kinldy provide your own APIKey
designId: '3d8f3899904ef2392795c681091600d0', //Change this to your designId
custom: true
});
//Function to run once the app is validated (ready to be used)
var appValidated = () => {
_launchButton.removeAttribute('disabled'); //Enable the Launch button
_launchButton.onclick = () => ppclient.showApp(); //Attach event listener to the button when clicked to show the app
_loaderDiv.style.display = 'none'; //Hide the loader
};
//Function to run once the user has saved their project
var projectSaved = (_val) => {
let _data = _val.data; //You can console.log the _data varaible to see all that's passed down
if (_data && _data.previews && _data.previews.length) {
_previewDiv.innerHTML = _data.previews.reduce((_str, _prev) => `${_str}<img src="${_prev}">`, ''); //Show the preview images
}
};
ppclient.on('app-validated', appValidated);
ppclient.on('project-saved', projectSaved);
return <div>
<div id="pp_loader_div"><img src="https://pitchprint.io/rsc/images/loaders/spinner_new.svg" /></div>
<button id="launch_btn" >Launch Designer</button>
<div id="pp_preview_div"></div>
</div>;
};
export default AppButton;
PS: I know getElementById does not realy work with react, I'll deal with that later, for now I just want to initialize this app.
that's because the component is not mounted yet.
you need to call document.getElementById once the component is mounted, and in order to access dom elements inside the component you need to call it inside useEffect hook
useEffect(() => {
let _launchButton = document.getElementById("launch_btn");
let _previewDiv = document.getElementById("pp_preview_div");
let _loaderDiv = document.getElementById("pp_loader_div");
_launchButton.setAttribute("disabled", "disabled");
var ppclient = new PitchPrintClient({
apiKey: "f80b84b4eb5cc81a140cb90f52e824f6", //Kinldy provide your own APIKey
designId: "3d8f3899904ef2392795c681091600d0", //Change this to your designId
custom: true,
});
//Function to run once the app is validated (ready to be used)
var appValidated = () => {
_launchButton.removeAttribute("disabled"); //Enable the Launch button
_launchButton.onclick = () => ppclient.showApp(); //Attach event listener to the button when clicked to show the app
_loaderDiv.style.display = "none"; //Hide the loader
};
//Function to run once the user has saved their project
var projectSaved = (_val) => {
let _data = _val.data; //You can console.log the _data varaible to see all that's passed down
if (_data && _data.previews && _data.previews.length) {
_previewDiv.innerHTML = _data.previews.reduce(
(_str, _prev) => `${_str}<img src="${_prev}">`,
""
); //Show the preview images
}
};
ppclient.on("app-validated", appValidated);
ppclient.on("project-saved", projectSaved);
}, []);
Read more about React hooks and their constraints.
https://reactjs.org/docs/hooks-intro.html
also make sure to access global variables using window.PitchPrintClient
also, make sure your app is mounted once the dom is ready. by moving your script tags to the end of the body tag or using jquery on ready callback.
PS: The answer is not considering the best practices of writing react components, but I encourage you to use refs and minimize accessing to dom as much as you could.

VueJs 2: How to use one component function in another component?

I have 2 Components and need to use a function with prop from component A in component B. The function just hides/shows HTML Element. However, it doesn't work as it should.
My settings Component:
Vue.component('settings', {
props: {
settingsContainer: {
type: String,
required: true
}
},
template: `
<div>
<button #click="toggleOverlay">Settings</button>
</div>
`,
methods: {
toggleOverlay: function() {
if (this.settingsContainer === 'block') {
this.settingsContainer = 'none';
}else if (this.settingsContainer === 'none') {
this.settingsContainer = 'block';
}
console.log(this.settingsContainer);
return this.settingsContainer;
}
}
});
The component where I use it:
Vue.component('sample-comp', {
template: `
<div>
<settings
:settings-container="displaySettings"
></settings>
<div :style="{ display: displaySettings }">
<p>Some Text</p>
</div>
</div>
`,
data: function() {
return {
displaySettings: 'block' //toggleOverlay should change its value
};
}
});
Main App:
new Vue({
el: '#app'
});
HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Title</title>
<script src="https://cdn.jsdelivr.net/npm/vue#2.6.12" defer></script>
<!-- <script src="https://unpkg.com/vue#next" defer></script> -->
<script src="test-settings-bt.js" defer></script>
</head>
<body>
<section id="app">
<sample-comp></sample-comp>
</section>
</body>
</html>
I see in console that toggleOverlay changes the value but it does not affect my style (does not change displaySettings value).
I would recommend using either an event or the Vuex Store to pass the settings value (Use the vuex Store only if this is a part of a bigger more complicated Application and the settings have other influences).
It is better practice to have a well defined API to a component and letting the component itself handle it's appearance instead of altering Component X's appearance directly in Component Y.
Additionally as Phil pointed out in the comments, you should never alter props. They should be handled as read only.
Here is what you could do:
Emit an event in your settings component when the display should be updated. Either have two events, one for showing the overlay, the other for hiding it or emit only one event with a boolean payload.
Listen to the event / events on the sample-comp and alter your css attributes accordingly.
Kind Regards.

ReactCssTransitionGroup use React-route.Link pageTransition

React.js
I have a problem with React.js code below.
I'm trying to set the animation before page transition using "React-router.Link." and ReactCSSTransitionGroup.
version
react: '15.2.1'
react-addons-css-transition-group: '15.2'
react-router: '2.6.0'
I want get the lifecycle event, so I can use JS not CSS.
If you know the right way to do, please let me know.
Thank you.
ex.) componentWillLeave etc...
P.S
I tried this code but componentWillLeave is not fire.
var React = require("react");
var ReactRouter = require("react-router");
var CSSTransitionGroup = require('react-addons-css-transition-group');
var Link = ReactRouter.Link;
var Test = React.createClass({
componentWillLeave: function(callback) {
console.log("component will leave");
$(this.getDOMNode()).hide(duration, callback);
},
render: function() {
return (
<div id="index">
<CSSTransitionGroup transitionName="example" transitionAppear={true} transitionLeave={true} transitionAppearTimeout={3000} transitionLeaveTimeout={3000}>
<Link to="/" key="toIndex">Index</Link>
<Link to="contact" key="toContact">Contact</Link>
</CSSTransitionGroup>
</div>
)
}
});
I fixed this issue by myself.
I didn't know ReactTransitionGroup.
Thanks, Evenryone.

Non blocking render in ReactJS

I'm learning ReactJS and trying to build some application on it.
When I'm trying to modify my state and render, my page is freezing and can't do anything until the render is finished when my components become huge.
I found that I can use shouldComponentUpdate to optimize my code, but the question comes to me is: Can I make this render procedure be non blocking? And so I can tell the user that the page is processing some heavy loading executions and please wait or maybe show the progress of the execution? Or if the user can cancel the render, for example, for a live editor, if user edit the content of the editor, the "preview" section will stop rendering old content and trying to render new content without blocking the editor UI?
Here's the heavy loading example code:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>React Tutorial</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.1/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.1/react-dom.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.24/browser.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
</head>
<body>
<div id="content"></div>
<script type="text/babel">
var Box = React.createClass({
render: function() {
return (
<div>Box</div>
);
}
});
var CommentBox = React.createClass({
getInitialState: function() {
return {box_count: 5};
},
heavyLoadRender: function() {
this.setState({box_count: 40000});
},
render: function() {
var render_box = [];
for (var i=0; i<this.state.box_count; i++) {
render_box.push(<Box />);
}
return (
<div>
{render_box}
<button onClick={this.heavyLoadRender}>Start</button>
</div>
);
}
});
ReactDOM.render(
<CommentBox />,
document.getElementById('content')
);
</script>
</body>
</html>
When I press Start, the page will freeze and no response until all Box is rendered. Is it possible to add a button named Cancel which user can cancel the render and clear all boxes?
This is a great question, and a perfect use case for setTimeout which can schedule an update to the next round of the event loop.
Rather than store the number of components to render, store an array of components, and render them directly. jsfiddle
var CommentBox = React.createClass({
getInitialState: function() {
return { boxes: [<Box key="first" />] };
},
heavyLoadRender: function() {
setTimeout(() => {
if (this.state.boxes.length < 50000) {
this.setState({
boxes: this.state.boxes.concat(<Box key={this.state.boxes.length} />)
})
this.heavyLoadRender()
}
})
},
render: function() {
return (
<div>
<button onClick={this.heavyLoadRender}>Start</button>
{this.state.boxes}
</div>
)
}
})
Update:
If you only want to show the state once the array is filled up, don't display anything until it hits that size:
This did not work:
{ this.state.boxes.length === 50000 && this.state.boxes }
Hope is not lost though! Use style!
<div style={{ display: this.state.boxes.length === 50000 ? 'block' : 'none' }}>
{ this.state.boxes }
</div>
If you want to increase the speed, you can push more than item per setTimeout
var newBoxes = []
for (var i = 0; i < 5; i++) {
newBoxes.push(<Box />)
}
this.setState({
boxes: this.state.boxes.concat(newBoxes)
})
updated fiddle. I think this whole class of problems is going to take time to perform. In batches of 10,000 the basic box component doesn't block and you could easily throw a loading spinner up there.

Is there any required div wrapper inside a React component

I create a menu using React JS:
var Dropdown = React.createClass({
render: function() {
return (
<Title />
<OptionsDropdown />
);
}
});
where Title and OptionsDropdown are other React classes.
The problem is this code has error until I wrap them around a div like :
var Dropdown = React.createClass({
render: function() {
return (
<div class="something">
<Title />
<OptionsDropdown />
</div>
);
}
});
Is there anyway better to handle this situation when I want no div is wrapped outside Title and OptionsDropdown.
Finally. I found out there is maximum one root node in render function in React JS. Better to wrap it with a div.

Categories

Resources