I have React.js app flavored with react-router, I have a doubt regarding my current routes handling.
Design looks as follows, common mobile layout, fixed header and footer, content in the middle:
In the case they are static I can simply create such structure:
<RatchetHeader />
<RatchetFooter />
<RouteHandler />
But occasionally they would change from page to page, for example:
title and button texts
number of buttons
footer is not present on some pages
Is it better to put them inside view controllers and re-render everytime with RouteHandler?
I don't know specifics of Ratchet, but in general terms of react, in your situation, it's better indeed for the footer to put it inside a RouteHandler, so that you can define its presence depending on your preferences.
For the Header, I believe you'd always like to have it there? In that case, you could leave it outside the Handler and pass it properties instead to change it's layout.
The final result would look something similar to this (the component imports are implied, therefore I'm not including them for the sake of keeping focus on the logic):
The app.js:
<Route handler={AppHandler}>
<DefaultRoute handler={HomeHandler}/>
<Route name='foo' path='/foo' handler={FooHandler}/>
<Route name='bar' path='/bar' handler={BarHandler}/>
<NotFoundRoute handler={NotFoundHandler}/>
</Route>
);
The App.react.js:
<div>
<Header title={this.state.title}/>
<RouteHandler {...this.props}/>
</div>
The Header.react.js - using some imaginary components for illustration:
var Header = React.createClass({
render: function(){
return (
<div>
<Button type="previous" title="Left"/>
<HeaderTitle>{this.props.title}</HeaderTitle>
<Button type="next" title="Right"/>
</div>
);
}
});
module.exports = Header;
The Foo.react.js:
var Foo = React.createClass({
render: function(){
var footerActions = [ // Ideally you'd get this from a constants file or something alike.
{
'actionType': 'viewHome',
'actionIcon': 'icon-home',
'actionLabel': 'Home'
},
{
'actionType': 'viewProfile',
'actionIcon': 'icon-profile',
'actionLabel': 'Profile'
},
{
'actionType': 'viewFavorites',
'actionIcon': 'icon-favorites',
'actionLabel': 'Favorites'
},
...
];
return (
<div>Your content here</div>
<Footer actions={footerActions}/>
);
}
});
module.exports = Foo;
The Footer.react.js:
var Footer = React.createClass({
render: function(){
var actionItems = this.props.actions.map(function(item){
return (<ActionItem action={item.actionType} icon={item.actionIcon} label={item.actionLabel}/>);
});
return (
<div>{actionItems}</div>
)
}
});
module.exports = Footer;
Then, in the Bar.react.js you could just not include the <Footer> component, like this:
The Bar.react.js:
var Bar = React.createClass({
render: function(){
return (
<div>Your content here</div>
);
}
});
module.exports = Bar;
Hope that helps!
Related
I am trying to copy this fiddle:
http://jsfiddle.net/jhudson8/135oo6f8/
(I also tried this example
http://codepen.io/adamaoc/pen/wBGGQv
and the same onClick handler problem exists)
and make the fiddle work for server side rendering, using ReactDOMServer.renderToString
I have this call:
res.send(ReactDOMServer.renderToString((
<html>
<head>
<link href={'/styles/style-accordion.css'} rel={'stylesheet'} type={'text/css'}></link>
</head>
<body>
<Accordion selected='2'>
<AccordionSection title='Section 1' id='1'>
Section 1 content
</AccordionSection>
<AccordionSection title='Section 2' id='2'>
Section 2 content
</AccordionSection>
<AccordionSection title='Section 3' id='3'>
Section 3 content
</AccordionSection>
</Accordion>
</body>
</html>
)));
the Accordion element looks like so:
const React = require('react');
const fs = require('fs');
const path = require('path');
const Accordion = React.createClass({
getInitialState: function () {
// we should also listen for property changes and reset the state
// but we aren't for this demo
return {
// initialize state with the selected section if provided
selected: this.props.selected
};
},
render: function () {
// enhance the section contents so we can track clicks and show sections
const children = React.Children.map(this.props.children, this.enhanceSection);
return (
<div className='accordion'>
{children}
</div>
);
},
// return a cloned Section object with click tracking and 'active' awareness
enhanceSection: function (child) {
const selectedId = this.state.selected;
const id = child.props.id;
return React.cloneElement(child, {
key: id,
// private attributes/methods that the Section component works with
_selected: id === selectedId,
_onSelect: this.onSelect
});
},
// when this section is selected, inform the parent Accordion component
onSelect: function (id) {
this.setState({selected: id});
}
});
module.exports = Accordion;
and the AccordionSection component looks like so:
const React = require('react');
const AccordionSection = React.createClass({
render: function () {
const className = 'accordion-section' + (this.props._selected ? ' selected' : '');
return (
<div className={className}>
<h3 onClick={this.onSelect}>
{this.props.title}
</h3>
<div className='body'>
{this.props.children}
</div>
</div>
);
},
onSelect: function (e) {
console.log('event:',e);
// tell the parent Accordion component that this section was selected
this.props._onSelect(this.props.id);
}
});
module.exports = AccordionSection;
everything works, and the CSS is working, but the problem is that the onClick doesn't get registered. So clicking on the accordion elements does nothing. Does anyone know why the onClick handler might not get registered in this situation?
React DOM render to string only sends the initial HTML as a string without any JS.
You need a client side react router as well which will attach the required JS handlers to the HTML based on their react-id's. The JS needs to run on both sides.
Universal rendering boilerplate for quick start. https://github.com/erikras/react-redux-universal-hot-example
Another question which is similar to yours. React.js Serverside rendering and Event Handlers
None of the hooks will register with ReactDOMServer.RenderToString. If you want to accomplish server side rendering + hooks on your react component, you could bundle it on the client (webpack, gulp, whatever), and then also use ReactDOMServer.RenderToString on the server.
Here's a blog post that helped me accomplish this:
https://www.terlici.com/2015/03/18/fast-react-loading-server-rendering.html
Let's assume i have a react component class that displays a modal dialog on a click of a button.
it can be created like this (in jsx):
<Modal text={"some text"}/>
Now, I have a bunch of component classes (let's call them Panels) that all have a function called getMessage, and i'd like the same behavior in all of these components: the modal dialog should show the string that returns from the call to getMessage.
the straight forward way to do this would be to include
<Modal text={this.getMessage()}/>
in the render() function for each such component.
Now, let's say that there is a bit more logic involved. for example, i would only like to render this component if getMessage is defined and does not return null.
Now this is starting to look like this:
var Panel1 = React.createClass({
getMessage: function() {return 'wow';},
render: function() {
var modal = null;
if (this.hasOwnProperty('getMessage' && this.getMessage() !== null) {
modal = <Modal text={this.getMessage()}/>
}
return (
<div>
{modal}
...all other stuff done in panel
</div>
);
}
});
This is starting to become cumbersome because I need to have this logic for each and every component class I define.
How can I achieve DRYness in this scenario so that i don't have to repeat this?
One way would be to define a utility function that contains this logic, let's call it displayModalIfNeeded and the call it from render. this now looks like this:
return (
<div>
{displayModalIfNeeded.call(this)}
....all other stuff needed in Panel
</div>
);
And now for my actual question (sorry for the long exposition):
Let's say that i have a parent component called <Dashboard> which has all panels as its childern:
<Dashboard>
<Panel1>
<Panel2>
<Panel3>
</Dashboard>
Is there something i can write in the implementation of Dashboard that will entirely remove the need to specify anything about these modal components in each Panel?
meaning the the Panel1 implementation can now just be
<div>
...all other stuff done in panel
</div>
and when it's rendered as a child of Dashboard it will have that modal dialog and accompanying logic.
I suggest using a wrapper component with the children prop. Your parent component would look like this:
<Dashboard>
<ModalWrapper text={msg1}>
<Panel1 />
</ModalWrapper>
<ModalWrapper text={msg2}>
<Panel2 />
</ModalWrapper>
<ModalWrapper text={msg3}>
<Panel3 />
</ModalWrapper>
</Dashboard>
Now all your conditional logic can be placed in ModalWrapper. Where your question has "....all other stuff needed in Panel", use this.props.children. e.g.
var ModalWrapper = React.createClass({
render: function () {
var text = this.props.text;
return (
<div>
{text ? <Modal text={text} /> : null}
{this.props.children}
</div>
);
}
});
I'm creating my first React app, so apologies in advance. A newbie must learn as he goes.
But I'm a few hours into debugging, having gotten nowhere, and I'm hoping someone can clarify why this attempt to pass data into a React component, use prototype.map, and render a final component just isn't cutting it.
var imagedata = [{"id":"1"},{"id":"2"},{"id":"3"}];
var portraitPhoto = React.createClass({
render: function() {
return (
<div className="test">
<img src={"./build/assets/images/photos/square_raw/" + this.props.imagepath + ".jpg"}
className="full-width-portrait" />
</div>
);
}
});
var portrait = React.createClass({
getDefaultProps: function(){
return {
data: imagedata
}
},
render: function() {
var portraitEach = this.props.data.map(function (imaged,i) {
return (
<div className="portrait2">
<portraitPhoto imagepath={imaged.id}/>
</div>
);
});
return (
<div className="portrait-container">
{portraitEach}
</div>
);
}
});
React.render(
<portrait/>,
document.getElementById('portraits')
);
You need your React components to have uppercase names. React's help page says:
React's JSX uses the upper vs. lower case convention to distinguish
between local component classes and HTML tags
Simply having this should be enough:
var PortraitPhoto = React.createClass({
...
...
});
A demo on jsfiddle is here.
I am bulding a web application that talks with some APIs in reactJS.
My app has 4 roles, admin, master, slave, baby.
Every role can see something more than the others, so the the menus, and the view are slightly different between them.
e.g.
The navbar has:
for the admin: 5 buttons
for the master: 4 buttons
for the salve: 3 buttons
for the baby: 2 buttons
I wanted to know what is the best practice for structuring this application:
Should I have 4 differents apps? This way the component would be clear, but everytime that I need to implement a new function I have to modify 4 apps.
If this is the only way, how can I call the right app based on the role after the login?
Having four separate apps with a lot of overlapping code/functionality would surely not be the best approach; that would result in a lot of unnecessary repetition.
If the different roles just add/remove small features and functionality, I would just wrap rendering of these elements in conditionals. For example:
renderAdminButton: function() {
( if this.props.user.role === 'admin' ) {
return (
<button />
);
}
}
This function would then be called inside the parent app/component's render function.
However, if you REALLY want to have a separate app for each role, you could conditionally render the app with a function similar to this:
renderApp: function() {
var apps = {
admin: AdminApp,
master: MasterApp,
slave: SlaveApp,
baby: BabyApp
};
return apps[this.props.user.role];
}
In your button component check for this.props.hide and return null.
render() {
if (this.props.hide) {return null;}
return (
<button>
{this.props.btn.text}
</button>
);
}
In your parent component include logic for each buttons hide prop.
render() {
var hideAdminBtn = this.props.appState.userRole != 'admin';
return (
<JButton hide={hideAdminBtn} />
);
}
What you'll want to do is make each access type as a prop so you can render the application based on the type of prop received, ideally at componentDidMount(). You can then have a MasterView that will receive the prop and decide accordingly which view to render. The code will follow along like this:
var MasterView = React.createClass({
var roleType = this.props.role;
render: function() {
if(roleType === 'admin') {
return (
<AdminView />
);
}
});
As for your concern about implementing new features, you should break down you view and logic into simple and reusable components. For example all your access type can share a common <Header /> and <Footer /> component as well as <Profile /> <Settings /> etc. Mind you these can be further broken down so that when you make a small change in any of the components it will propagate to all your views. Similar principles will apply to your logic as well. Remember to stay DRY.
Edit:
Let's assume that the app is being made for a store. All the data displayed via this portal(indicted by the component's name) are:
<EmployeeProfile />
<EmployeeSalary />
<BuyerRecords />
<BalanceSheet />
<Inventory />
<Billing />
Now let's define the access level of each roles:
Admin has access to all the functionalities of the app.
Master can view all but the employee's salary details.
Slave has access to buyer's record, inventory and billing.
Child can only view the inventory and billing sections.
Now, these 6 broad components will be defined and will definitely comprise of multiple components. Just make sure that whenever you feel like a specific part of the view will be used elsewhere, go ahead and make it into a separate component.
Finally, the roles should render like this:
var AdminView = React.createClass({
render: function() {
return (
<div>
<EmployeeProfile />
<EmployeeSalary />
<BuyerRecords />
<BalanceSheet />
<Inventory />
<Billing />
</div>
);
}
});
var MasterView = React.createClass({
render: function() {
return (
<div>
<EmployeeProfile />
<BuyerRecords />
<BalanceSheet />
<Inventory />
<Billing />
</div>
);
}
});
var SlaveView = React.createClass({
render: function() {
return (
<div>
<BuyerRecords />
<BalanceSheet />
<Billing />
</div>
);
}
});
var ChildView = React.createClass({
render: function() {
return (
<div>
<Inventory />
<Billing />
</div>
);
}
});
This way you have 4 parent components handling all the views. They comprise of multiple components and in case a change needs to made, it needs to be done only once and it will reflect in all the roles.
If you decide to have a slightly modified <Inventory /> for each role, you can choose to render different child components of <Inventory /> as determined by the role.
Go ahead and have a look at this official blog post as well. Should help clear any doubts :)
Using React 0.12.2 and given a layout component, e.g. a tray:
<div className="tray">
<div className="tray__item tray__item--left" data-width="260px">
Load a component in the left tray
</div>
<div className="tray__item tray__item--center">
Load a component in the center tray
</div>
<div className="tray__item tray__item--right" data-width="100%">
Load a component in the right tray
</div>
</div>
I would like to be able to insert arbitrary components into each of the contents, passing them as args to this component.
Perhaps something like:
<Tray left={Component1} center={Component2} right={Component3}/>
I would also like to know how to pass an unknown amount of components e.g:
<Carousel items={Component1,Component2,Component3,Component4}/>
Just to be clear - these container components are "dumb" - they only care about sliding content - you should be able to pass whatever content (components) you want to them.
How can I do that and then render them? Thanks.
In the render method of Tray you can do
render: function() {
return (
<div className="tray">
{this.props.children}
</div>
);
}
Then in the component where your Tray lives you can do
<Tray>
<TrayItem position="left"/>
<TrayItem position="center"/>
<TrayItem position="right"/>
</Tray>
You should be able to keep nesting this pattern, i.e.
<Tray>
<TrayItem position="left">
<SomeComponent/>
</TrayItem>
<TrayItem position="center">
<div>
<AnotherComponent/>
</div>
</TrayItem>
<TrayItem position="right"/>
</Tray>
In this case TrayItem's render should also include {this.props.children}
The general principle is, you can put arbitrary components inside other components as long as the container component's render includes {this.props.children}.
Thanks for the answer Adam Stone + SimpleJ.
var Tray = React.createClass({
render: function () {
return (
<div className="tray">
{this.props.children}
</div>
);
}
});
var TrayItem = React.createClass({
render: function () {
return (
<div className="tray__item">
{this.props.children}
</div>
);
}
});
<Tray>
<TrayItem>
<ComponentA/>
<ComponentAB/>
</TrayItem>
<TrayItem>
<ComponentB/>
</TrayItem>
<TrayItem>
<ComponentC/>
</TrayItem>
</Tray>
You should just create a container component that has multiple child components in its render function. You never want to pass a component in as a prop