Show and hide a component based on a variable - javascript

I have created a loading icon component, which simply displays a spinner while loading something. I load it into my Sign In component, and wish to display the icon once the user clicks the Login button (And the API call is busy).
So:
import Loading from '../common/loading';
I then set an isLoading variable, defaulted to false:
this.isLoading = false;
Then, within my render method, I wish to determin if I need to show the spinner or not.
render() {
var LoadingSpinner = this.state.canLogin ? Loading : '<div></div>';
This fails.
And then my button is where I show the spinner. I'm hoping to hide the 'Sign In' text, and replace it with the spinner, but ... first thing is to handle the spinner, based on the isLoading variable.
<button
className="btn btn-lg btn-primary btn-block"
type="button"
onClick={this.handleSignin}
disabled={!this.state.canLogin}>
<span>Sign in</span> <LoadingSpinner />
</button>
</div>
Can/should this be done this way, OR... should I maybe pass a prop to my Loading component, called 'Visible' or something, and I set that?

put isLoading to constructor with default false
and then inside the render method, just add a condition
{ this.state.canLogin ? <LoadingSpinner /> : null }

Here is what you could do, using a state variable.
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
loading: false
}
}
onClick = () => {
this.setState({
loading: true
})
}
render() {
return (
<div>
{this.state.loading && <div>Loading</div>}
<button onClick={this.onClick}>Click to Load</button>
</div>
);
}
}
ReactDOM.render( < App / > , document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id='root'>
</div>

Related

Unexpected token '<' even when text/babel is used on script

I'm trying to render a button in React using perfectly valid JSX (at least, as far as I can tell after looking at it for 20 minutes). But for some reason I keep getting the syntax error:
Unexpected token '<' on line 18
This comes as a surprise to me considering when I load the script I'm both using the text/babel type attribute, and the babel script is loaded in the <head> while this one is loaded in the DOM.
Here's what my HTML looks like:
<head>
<!-- react, react-dom, axios etc. go here !-->
<script crossorigin src="https://unpkg.com/babel-standalone#6.26.0/babel.min.js"></script>
</head>
<body>
<div id="content"></div>
<!-- notice that i'm using babel !-->
<script type="text/babel" src="cdn/static/scripts/module/user.js">
</body>
And here is my JavaScript:
class User extends Component{
constructor(props){
super();
this.user = props.user;
this.state = {
modalOpen: false
};
}
Button = () => {
let user = this.user;
// vvvv this is what the error points to
return <button className={(user.loggedIn ? "user" : "sign-in")}>{(user.loggedIn ? user.public.display : "Sign In")}</button>;
}
render(){
return (
<div className="button">
<this.Button />
</div>
);
}
}
ReactDOM.render(<User user={window.User} />, document.getElementById("content"));
What is even stranger is that it will actually render the button, but because there is an error, it will also cause the rest of my JavaScript to break.
I've never come across this problem before so all help is appreciated, cheers.
You don't have React scripts attached see how to create React app.
Because JSX is syntatic sugar for React.createElement, and custom components must be Uppercased, you should render Button like so:
class User extends Component{
constructor(props){
super();
this.user = props.user;
this.state = { modalOpen: false };
}
Button = () => {
let user = this.user;
return (
<button className={user.loggedIn ? "user" : "sign-in"}>
{user.loggedIn ? user.public.display : "Sign In"}
</button>
);
}
render(){
const Button = this.Button;
return (
<div className="button">
<Button />
</div>
);
}
}
Or invoke React.createElement directcly:
<div className="button">
{React.createElement(this.Button, null)}
</div>
See JSX in Depth.

Manipulate HTML5 dialog in React

I'm trying to create a simple configuration dialog with close icon on top-right, but can't think of how to handle this in React. In other frameworks, I can simply use selector, and then use .showModal()/close() to open/close a dialog. However, I think we're not allowed, or not recommended to directly manipulate DOM in React, so I wonder what's the proper way in this case.
My project outline
App.js
class App extends Component {
...
...
return(
<div>
<ConfigPage />
<ConfigButton />
<MainContents />
</div>
)
}
I want to open a dialog, which is <ConfigPage />, by pressing the <ConfigButton /> I set, and close it by pressing the icon on the dialog.
config-page.js
class ConfigPage extends Component {
...
...
return(
<dialog>
<header>
<div>
<i onClick={someCallback}></i>
</div>
</header>
<section></section>
</dialog>
)
}
the HTML5 dialog also has an open attribute, correct? Instead of calling show/hide you could manipulate this attribute -
class ConfigPage extends Component {
...
...
return(
<dialog open={this.state.showDialog ? 'open' : false}>
<header>
<div>
<i onClick={someCallback}></i>
</div>
</header>
<section></section>
</dialog>
)
}
And when you want to show/hide call this.setState({showDialog: true}) (or false)
Here's a js fiddle with a proof-of-concept: https://jsfiddle.net/n5u2wwjg/193969/
Welcome to SO. You can hide a react component by return null from the render function. You can define a flag in the state that determines weather or not your component is visible. Here is a simple example.
class Modal extends Component {
constructor(props) {
super(props);
this.state = {
isOpen: true;
};
this.onCloseClick = this.onCloseClick.bind(this);
}
onCloseClick(e) {
e.preventDefault();
this.setState({
isOpen: false,
});
}
render(){
if (!this.state.isOpen) return null;
return (
<div>
<button onClick={this.onCloseClick}>
Close
</button>
<h1>What up, this a modal</h1>
<div>
);
}
}

How do you change what jsx blocks are being rendered from inside a same class?

I'm still learning the basics of React, and I wanted to do something that caught my attention. It's about rendering two things. Is it possible or even plausible to just change what is rendered with a function, and then calling the functions separately with a button or timer?
This is a sample code, to show how it would render two completely different things. It is supposed to render a button that says "State: On" or "State: Off". And when you click the button the state changes. But also, the whole render method is switched... or at least that's what's supposed to be happening.
class Flicker{
constructor(props){
super(props);
this.state = {mode: "on"};
}
flipOn(){
this.setState({mode: "on"})
}
flipOff(){
this.setState({mode: "off"})
}
if (this.state.mode == "on"){
render() {
return(
<button onClick={this.flipOn}>State: On</button>
);
}
} else if (this.state.mode == "off"){
render() {
return(
<button onClick={this.flipOff}>State: Off</button>
);
}
}
}
export default Flicker;
If this isn't the correct way to do this type of changes in what jsx gets rendered on the app, how should it be done?
You have the right idea - but in a class there is only one render method. Your logic does belong inside the render. This should do what you're looking for:
class Flicker extends React.Component {
constructor(props){
super(props);
this.state = {mode: "On"};
}
flipOn(){
this.setState({mode: "On"})
}
flipOff(){
this.setState({mode: "Off"})
}
render () {
return (
<button onClick={(this.state.mode === 'Off') ? this.flipOn : this.flipOff}>State: {this.state.mode}</button>
)
}
}
export default Flicker;
Put the conditional logic inside the render() method.
Something like this...
class Example extends React.Component {
// the rest of your code
render() {
const { mode } = this.state;
return(
<div>
{mode ==="on" && <button onClick={this.flipOn}>State: On</button> }
{mode === "off" && <button onClick={this.flipOff}>State: Off</button>}
</div>
)
}
}
Your component changes the state. The state is boolean - either on (true) or not (false). Since you're state is switched between two value, you can use a single method (toggle). Since we check the previous state, it it's better to setState via updater.
You need to bind the method to this, by using bind in the constructor (method 4) or an arrow function via an instance method (method 5).
Now in the (single) render, you only need to change the text according to the state of on:
class Flicker extends React.Component{
state = { on: true };
toggle = () =>
this.setState(({ on }) => ({ on: !on }));
render() {
const text = this.state.on ? 'On' : 'Off';
return (
<button onClick={this.toggle}>State: {text}</button>
);
}
}
ReactDOM.render(
<Flicker />,
demo
);
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id="demo"></div>
Real world example
The button should probably get on and toggle via props. Now they are available outside, and the Flicker's only concern is calling toggle when it's clicked, and changing the text according to on:
const Toggler = ({ on, toggle }) => (
<button onClick={toggle}>
State: {on ? 'On' : 'Off'}
</button>
);
class Flicker extends React.Component {
state = { on: true };
toggle = () =>
this.setState(({ on }) => ({ on: !on }));
render() {
const { on } = this.state;
return (
<div>
<Toggler toggle={this.toggle} on={on} />
{on &&
'I\'m displayed only when I\'m On'
}
</div>
);
}
}
ReactDOM.render(
<Flicker />,
demo
);
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id="demo"></div>

Dynamically Creating a React Modal Using Portals

How can I append a component to a modal using a nested portal?
I am building a page that displays several modals. I want to reuse the same modal but when a button is clicked, the app loads different content into the modal.
WIP CodePen:
https://codepen.io/jtsharpsite/pen/gorvjR
render() {
return ReactDOM.createPortal(
this.props.children,
domNode,
);
}
I have it pretty close to how I think it might work, but I cannot figure out how to append another component to an already appended modal component.
I have a button which calls a handler that opens the modal and specifies the component type.
<button onClick={this.handleShow.bind(this, "p3009", "product")}>
Product 3009
</button>
<button onClick={this.handleShow.bind(this, "s1", "special")}>
Special #1
</button>
The handler is in the App context and opens the modal sibling:
handleShow(modalId, modalType) {
this.setState({ showModal: true });
}
When the modal component mounts, I then try to append the Product when it mounts.
componentDidMount() {
//TODO how to append to parent modal?
modalRoot.appendChild(this.el);
}
How can I move the <Product> or the <Special> up into the <Modal>?
Here's an example of a reusable modal form... Theres a lot here you don't need, but just pay attention to the handleSubmit. Within the handleSubmit we call onComplete() which can be any function we pass through to the reusable modal when we call it, based on what function we want our modal to do... in our case it was capturing username, password, etc.
class ReusableModalForm extends React.Component {
constructor(props){
super(props);
this.state ={
};
}
handleChange(e) {
let {name, value} = e.target;
this.setState({
[name]: value,
usernameError: name === 'username' && !value ? 'username must have a value' : null,
emailError: name === 'email' && !value ? 'email must have a value' : null,
passwordError: name === 'password' && !value ? 'password must have a value' : null,
});
}
handleSubmit(e) {
e.preventDefault();
this.props.onComplete(this.state)
}
render() {
return (
<Modal
open={this.state.createAccountModalOpen}
trigger={<Link size="m" theme="bare" href="#" className="main-menu-item" onClick={this.handleSubmit}>{this.props.buttonText}</Link>}
closeIcon
onClose={() => { this.setState({
createAccountModalOpen: false,
}); }}
>
<Header icon='add user' content='Create account' />
<Modal.Content>
<Form />
</Modal.Content>
<Modal.Actions>
<Button color='green' onClick={this.handleSubmit}>
<Icon name='add user' /> {this.props.buttonText}
</Button>
</Modal.Actions>
</Modal>
);
}
}
export default ReusableModalForm;
Based on this logic, you could construct a modal with a series of "trigger texts" to only render certain things based on what kind of props are passed down when you use your reusable modal.
Example.
<ReusableModal triggerText={'showAdress'} onComplete={this.showUsersHomeOnGoogle} />
Then in your reusableModal somewhere....
{this.props.triggerText === showAdress ? this.setState=({showHomeAdressPortion: true)}
UPDATES
class App extends React.Component {
constructor(props) {
super(props);
this.state = { showModal: false };
this.handleShow = this.handleShow.bind(this);
this.handleHide = this.handleHide.bind(this);
}
handleShow(modalId, modalType) {
console.log(
"Append content with ID: " + modalId + " of component type: " + modalType
);
//Here you should do a "fetch" first and grab your entire product information based on chosen ID and store it to state as an object...
//setState({activePortal: someContentObject})
//Then... I would use promise or aync await, show modal...
//Have Modal display from this state object
this.setState({ showModal: true, activePortal: });
//if it is a product
if (modalType == "product") {
//fetch additional images and add to state
//add images to {product.additionalPics}
}
//TODO re-render the modal with the appending element portal
}
handleHide() {
this.setState({ showModal: false });
}
render() {
//console.log("RENDER App");
// Show a Modal on click.
const modal = this.state.showModal ? (
<Modal>
<div className="modal">
<div className="modal-content-wrapper">
{/* I think what you're asking to do here is display basically "any" information that comes from a product fetch. This isnt possible without templeting somehow.. You will have to hard code like "title" "body" "image" into this modal...
<h1>this.state.activeProduct.title</h1>*/}
{/* CONTENT NEEDS TO GO HERE SOMEHOW */}
{this.props.children}
</div>
<button onClick={this.handleHide}>Hide modal</button>
</div>
</Modal>
) : null;
Have you tried using a parent-child pattern in the portal?
For example, make your children like this and just have ModalDisplay render its children. I don't know if what you are trying to do is possible because I think it requires portals to support fragments and I'm not sure if they do.
I don't know if I got the context of your question right but the idea is the same you have a single child that itself has children instead of having more than 1 child in the portal call.
<ModalDisplay>
<Special title="special" text="text" />
<Product title="product" pic="picture" />
</ModalDisplay>

React: create dismissable Alert component

I'm trying to create a React component that will use bootstrap alert classes to display errors. The problem is that I want to make it dismissable, but attaching a handler in the close button inside the alert div to hide it will not make it re-render if I need to display the error again. Example:
class Alert extends React.Component {
constructor(props) {
super(props)
this.handleClick = this.handleClick.bind(this)
this.state = {
display: true
}
}
handleClick() {
this.setState({
display: false
})
}
render() {
return (
<div>
{this.state.display &&
<div className={"alert alert-" + this.props.type + " alert-dismissible mt10"}>
{this.props.message}
<button type="button" className="close" onClick={this.handleClick} data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>
</div>
}
</div>
)
}
}
This is a working code for the Alert component, and clicking in the close button inside it will hide it. Here's the issue:
class FormImageUpload extends React.Component {
...
render() {
return (
<form>
<input type="text" placeholder="Paste Image Url"/>
{this.props.displayUploadError &&
<Alert type="danger" message="There was an error trying to process the image. Please try again." />
}
<button type="submit" className="btn btn-primary mt10">SEND</button>
</form>
)
}
}
root parent:
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
displayUploadError: false
}
this.handleRequest = this.handleRequest.bind(this)
}
handleRequest(image_url) {
this.setState({
displayUploadError: true
})
}
render() {
return (
<div className="demo__wrap">
<FormImageUpload
handleRequest={this.handleRequest}
displayUploadError={this.state.displayUploadError}
/>
</div>
)
}
}
I have a boolean that indicates if I need to show/hide the Alert component. But if I closed the alert, it'll not be displayed again. How can I fix this?
Rather than setting the state of the alert, set the state in the form and pass the state as props. It should work then.
And onclick of the alert update the state of the form upload component, using a callback function.
Yes passing a boolean can also hide it.
Here is a mock code
If (boolean) {
Return Alert
}
Else {
Return null
}

Categories

Resources