Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
I am currently creating with React a login/register template which mimics in functionality the groupon login. Specifically, I am targeting the switching of the colored border when toggling between i have an account and i'm a new customer. The template is very common in today's development, and I have seen many poorly coded solutions which ignore best practices altogether.
I'll start by stating that the best solution uses state.
I would then create the following (incomplete, obviously):
<div className="wrapper">
<div className="login" />
<div className="register" />
</div>
I then thought to give both divs a state, and a border-bottom spec to render the colored border. The issue I am seeing is how to best implement the toggle of which one is active.
Another thought experiment is to hardcode everything up to the input fields. Then, when the user clicks on the I'M A NEW CUSTOMER field, all I have to do is set an active state to that component, and render .
Again, the point of this question is to answer how to best implement this login/registration form. By the way, Groupon gives their showing component a class of active.
EDIT: As was pointed out by Sunny Wong, and reflective of my assumption, his solution uses state and implements with my component as follows:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
activeTab: "login"
};
}
handleActiveTab = name => event => {
this.setState({ activeTab: name });
};
render() {
const { activeTab } = this.state;
let joinComponent =
activeTab === "login" ? <LoginComponent /> : <RegisterComponent />;
return (
<div className="wrapper">
<div
onClick={this.handleActiveTab("login")}
className={classNames("login", {
active: activeTab === "login"
})}
>
Login
</div>
<div
onClick={this.handleActiveTab("register")}
className={classNames("register", {
active: activeTab === "register"
})}
>
Register
</div>
{joinComponent}
</div>
);
}
}
Also, the post was tagged as too broad, yet I targeted specific topics/questions. To summarize, they were:
Specifically, I am targeting the switching of the colored border when
toggling between i have an account and i'm a new customer.
answer how to best implement this login/registration form
See the live version for further reference.
Toggling the classes is the way to go here. There is no need to re-render the tabs as different components, as the content is the same. The tabs only undergo a cosmetic change. There is a library called classNames (https://www.npmjs.com/package/classnames), which is extremely helpful in controlling which classes get applied in a given situation.
Something like keeping state of which link is active via an id. Then toggle an 'active' class which is responsible for applying the active styling.
state = { activeTab: 'login' }
handleActiveTab = e => {
//sets state of activeTab
}
render() {
const { activeTab } = this.state;
return (
<div className="wrapper">
<div onClick={this.handleActiveTab} className={classNames('login', { active: activeTab === 'login' })} />
<div onClick={this.handleActiveTab} className={classNames('register', { active: activeTab === 'register' })} />
</div>
);
}
<div className="wrapper">
<div className={`login ${this.state.active==CONST.LOGIN? 'active':''}`} onClick={e=>{this.setState({active:CONST.LOGIN}}/>
<div className={`register ${this.state.active==CONST.REGISTER? 'active':''}`} onClick={e=>{this.setState({active:CONST.REGISTER}} />
</div>
you have to maintain state for active in active that will allow you to load right component (login/register). CONST.LOGIN and CONST.REGISTER are constants that you have to manage. you can use just 1/2 the dirty way.
Related
I'm in a learning phase of react and I've been trying to nest style of parent in component in child component via class in react.js. How to do it?
What I've tried until now:
function Card(props) {
const classes = 'card' + props.className;
return (
<div className={classes}>
{props.children}
</div>
)
}
export default Card
I applied style on class 'card' in a CSS file.
Parent component JS code:
import Card from './UI/Card';
function ExpenseItem(props){
return (
<Card className="expense-item">
<ExpenseDate date={props.date} />
<div className="expense-item__description">
<h2>{props.title}</h2>
<div className="expense-item__price">{`₹ ${props.amount}`}</div>
</div>
</Card>
)
}
Now, there are various classes in parent component that are styled accordingly. And I'd like to nest those classes in child component for those style to work.
You can see in the code the way I tried to nest the classes, but it isn't working.
const classes = 'card' + props.className;
What am I doing wrong? And how should I correct it?
You missed a space after the card class name, otherwise i don't see any other issues in your code. Try below, hope it works
const classes = 'card ' + props.className;
Just one note, this will make your components dependent on each other. One of best things about React is that it allows you to have separation of concerns. So Card component is doing Card component staff, displaying some information and needs not be dependent on a parent in this case ExpenseItem.
One way to do it is to use styled-components.
I have a component that may or may not be rendered in a wrapper component:
class Video extends Component {
state = { isFullscreen: false }
render () {
const { isFullscreen } = this.state
return (
<View>
{isFullscreen ? (
<Modal>
<VideoView />
</Modal>
) : (
<VideoView />
)}
<Button title='inline' onPress={() => this.setState({ isFullscreen: false })} />
<Button title='fullscreen' onPress={() => this.setState({ isFullscreen: true })} />
</View>
)
}
}
Every time I press inline or fullscreen, the <VideoView /> remounts. This makes it hard to add initialization logic to the componentWillMount method. I can do other checks in my application to make sure it works well, but it feels better to re-use the component that already exists.
Is there a way to recycle the <VideoView />?
Ps My project is in React Native, so I used some syntax / component from RN, but I assume the question goes for normal React projects as well
Try wrapping the component with a wrapper and now toggle the styles for this wrapper based on state change. I guess that should work.
Also if you are imminent on reusing the modal component only, what you can do is define some CSS under a new className on the modal Component that alters the required styling to give an effect that the modal is toggled. Then add remove the className based on the state change.
I'm developing my wrapper over flexbox in react: https://github.com/aush/react-stack. I have a top level component that creates a container div and sets flex properties to it and also I map over it's children and set flex properties to them (I don't create additional wrapper divs for children, just set flex properties directly to them). And it's works flawlessly except one case.
My component, stripped down:
export default ({ someprops, style, children, ...rest }) =>
<div style={{ ...style, ...flexContainerStyle }} {...rest}>
{
React.Children.map(children, (child, i) =>
React.cloneElement(child,
{ key: i, style: { ...child.props.style, ...flexItemStyle } }))
}
</div>;
Consider this simple example, when all children are just standard divs:
codepen example 1
ReactDOM.render(
<Horizontal>
<div className="box1">1</div>
<div className="box2">2</div>
<Vertical>
<div className="box3">3</div>
<Horizontal grow={1}>
<div className="box4" align={'end'}>4</div>
<div className="box5" align={'end'}>5</div>
<div className="box6" align={'end'}>6</div>
</Horizontal>
</Vertical>
</Horizontal>,
document.getElementById('app')
);
Now, let's say I have these two react components: BuggedComponent and NotBuggedComponent and put them as children:
codepen example 2
const BuggedComponent = ({ children }) =>
<div className="box5">{children}</div>;
const NotBuggedComponent = ({ children, ...rest }) =>
<div className="box6" { ...rest }>{children}</div>;
ReactDOM.render(
<Horizontal>
<div className="box1">1</div>
<div className="box2">2</div>
<Vertical>
<div className="box3">3</div>
<Horizontal grow={1}>
<div className="box4" align={'end'}>4</div>
<BuggedComponent align={'end'}>5</BuggedComponent>
<NotBuggedComponent align={'end'}>6</NotBuggedComponent>
</Horizontal>
</Vertical>
</Horizontal>,
document.getElementById('app')
);
In this case, box 5 doesn't have any properties besides ones BuggedComponent sets itself.
It appears that properties set to the 'bugged' component are lost, obviously, because this component, essentially, discards rest-properties. For me it's a clear bug, not even just a bad practice. But I've taken a look at some github repos and I see that this quite a common practice and I cannot find any articles/guides which state this issue and point it as a bad practice.
The only argument for not passing rest that I have is that it's not clear, where rest-props should be passed. But since a react component can has only one root, it's rather natural that rest-props should be passed to it.
So, the question is: is it really, unfortunatelly very common, bad practice, or there is some explanation to why discarding rest-properties is the right way to develop react components?
It comes down to how much control over own child components does the dev intends to hand out to the consumers. In this case the BuggedComponent is generating a child div and it makes sense for it to pass on the rest properties to it. However, if the child would have been a more complex control where allowing arbitrary attributes does not make sense, there won't be a need for passing them down. Yours is a very specific case where you need absolute control over what class the child control has, but this may not be true in a general scenario. A good example is the very popular material-ui library, if you look at any complex control, it minutely controls the allowed props and almost never passes them down. It could make keeping inner-markup change-proof very difficult, apart from the simplest of cases.
I'm having trouble finding relevant documentation on how to remove UI components when using react. For example, there's a login form. The user clicks submit and now the form should be removed from the screen. How do I do this?
I've found unmountComponentAtNode, but that can only be invoked at the parent level. Am I supposed to have a parent node that is aware of all child state and loads them conditionally? Should all children have an "isHidden" attribute which renders the dom as hidden if true?
This must be basic but I don't see this in the react js tutorials. I found this stackoverflow post (react.js: removing a component) is this really the pattern? It kind of makes sense but it means that a large app will likely have an extremely complex Application parent class that manages maps of application state based on configuration.
It seems like i need to start defining application state as named maps. For example:
BaseApp: showHeader=true;showContent=true;
LoginState: showBaseApp=true;showLoginForm=true;
LoggedInState: showBaseApp=true;showFeed=true;
At any moment we would have to update all state maps and call the base class render method...
In my opinion your question isn't about removing component but about showing the right component. And yes - it can be done with a component state but with Flux/Redux store/reducer as well.
In your example with a login form after click on "Submit" we can change local state for the component and show another text like "The request was sent blah-blah-blah" or another component.
But you can do this by extracting component's local state to a store/reducer and it'll be work better in relatively big app. Nevertheless, it's really up to you where you want to store state.
I like to use a hide prop like so.
class AppCtrlRender extends React.Component {
render() {
let page = this.state.appState.currentPage;
let hideAbout = (page != 'about');
let hideHome = (page != 'home');
return (
<div id='AppCtrlSty' style={AppCtrlSty}>
<div id='allPageSty' style={allPageSty}>
<AboutPage hide={hideAbout} />
<HomePage hide={hideHome} />
</div>
</div>
);
}
}
export default class AboutPage extends React.Component {
render() {
if (this.props.hide) return null;
let aTime = (new Cache()).time.toString();
return (
<div style={AboutPageSty}>
React 0.14 ReFlux used for app state. This is the About Page.
<NavMenu /><br/><br/>
{aTime}
</div>
);
}
}
I've been hiding/showing react components by not rendering them, for example:
render: function() {
var partial;
if (this.state.currentPage === 'home') {
partial = <Home />;
} else if (this.state.currentPage === 'bio') {
partial = <Bio />;
} else {
partial = <h1>Not found</h1>
}
return (
<div>
<div>I am a menu that stays here</div>
Home Bio
{partial}
</div>
);
}
but just say that the <Bio/> component has lots of internal state. Everytime I recreate the component, it loses it's internal state, and resets to it's original state.
I know of course that I could store the data for it somewhere, and pass it in via props or just globally access it, but this data doesn't really need to live outside of the component. I could also hide/show components using CSS (display:none), but I'd prefer to hide/show them as above.
What's the best practice here?
EDIT: Maybe a better way to state the problem is to use an example:
Ignore React, and assume you were just using a desktop app that had a configuration dialog with a Tab component called A, which has 2 tabs, named 1 and 2.
Say that tab A.1 has an email text field and you fill in your email address. Then you click on Tab A.2 for a second, then click back to Tab A.1. What's happened? Your email address wouldn't be there anymore, it would've been reset to nothing because the internal state wasn't stored anywhere.
Internalizing the state works as suggested in one of the answers below, but only for the component and it's immediate children. If you had components arbitrarily nested in other components, say Tabs in Tabs in Tabs, the only way for them to keep their internal state around is to either externalize it somewhere, or use the display:none approach which actually keeps all the child components around at all times.
It just seems to me that this type of data isn't data you want dirtying up your app state... or even want to even have to think about. It seems like data you should be able to control at a parent component level, and choose to either keep or discard, without using the display:none approach and without concerning yourself with details on how it's stored.
One option would be to move the conditional inside the component itself:
Bio = React.createClass({
render: function() {
if(this.props.show) {
return <p>bio comp</p>
} else {
return null;
}
}
});
<Bio show={isBioPage} />
Whether this is "best practise" or not probably depends on the exact situation.
Unfortunately, style={{display: 'none'}} trick only works on normal DOM element, not React component. I have to wrap component inside a div. So I don't have to cascade the state to subcomponent.
<div className="content">
<div className={this.state.curTab == 'securities' ? 'active' : ''}>
<Securities />
</div>
<div className={this.state.curTab == 'plugins' ? 'active' : ''}>
<Plugins />
</div>
</div>
Looks like official documentation suggests hiding stateful children with style={{display: 'none'}}
The fundamental problem here is that in React you're only allowed to mount component to its parent, which is not always the desired behavior. But how to address this issue?
I propose the solution, addressed to fix this issue. More detailed problem definition, src and examples can be found here: https://github.com/fckt/react-layer-stack#rationale
Rationale
react/react-dom comes comes with 2 basic assumptions/ideas:
every UI is hierarchical naturally. This why we have the idea of components which wrap each other
react-dom mounts (physically) child component to its parent DOM node by default
The problem is that sometimes the second property isn't what you want
in your case. Sometimes you want to mount your component into
different physical DOM node and hold logical connection between
parent and child at the same time.
Canonical example is Tooltip-like component: at some point of
development process you could find that you need to add some
description for your UI element: it'll render in fixed layer and
should know its coordinates (which are that UI element coord or
mouse coords) and at the same time it needs information whether it
needs to be shown right now or not, its content and some context from
parent components. This example shows that sometimes logical hierarchy
isn't match with the physical DOM hierarchy.
Take a look at https://github.com/fckt/react-layer-stack/blob/master/README.md#real-world-usage-example to see the concrete example which is answer to your question (take a look at the "use" property):
import { Layer, LayerContext } from 'react-layer-stack'
// ... for each `object` in array of `objects`
const modalId = 'DeleteObjectConfirmation' + objects[rowIndex].id
return (
<Cell {...props}>
// the layer definition. The content will show up in the LayerStackMountPoint when `show(modalId)` be fired in LayerContext
<Layer use={[objects[rowIndex], rowIndex]} id={modalId}> {({
hideMe, // alias for `hide(modalId)`
index } // useful to know to set zIndex, for example
, e) => // access to the arguments (click event data in this example)
<Modal onClick={ hideMe } zIndex={(index + 1) * 1000}>
<ConfirmationDialog
title={ 'Delete' }
message={ "You're about to delete to " + '"' + objects[rowIndex].name + '"' }
confirmButton={ <Button type="primary">DELETE</Button> }
onConfirm={ this.handleDeleteObject.bind(this, objects[rowIndex].name, hideMe) } // hide after confirmation
close={ hideMe } />
</Modal> }
</Layer>
// this is the toggle for Layer with `id === modalId` can be defined everywhere in the components tree
<LayerContext id={ modalId }> {({showMe}) => // showMe is alias for `show(modalId)`
<div style={styles.iconOverlay} onClick={ (e) => showMe(e) }> // additional arguments can be passed (like event)
<Icon type="trash" />
</div> }
</LayerContext>
</Cell>)
// ...