React: create dismissable Alert component - javascript

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
}

Related

How to make buttons act as radio buttons in React.js by changing states?

I am fairly new to React and wanted to try my hand at making a pathfinding visualizer. I want to have buttons that can be clicked which would make that particular pathfinding algorithm active while all else are inactive but I am unable to achieve this.
I have an Options component which renders the buttons:
export class Options extends React.Component {
constructor(props) {
super(props);
this.state = {
dijkstra: false,
aStar: false,
bestSearch: false,
bfs: false,
dfs: false,
};
this.handleClick = this.handleClick.bind(this);
}
handleClick(name) {
this.setState({
dijkstra: false,
aStar: false,
bestSearch: false,
bfs: false,
dfs: false,
});
this.setState({ [name]: true });
}
render() {
return (
<div className='toolbar'>
<div className='algorithms'>
<Button
name="Dijkstra's Algorithm"
type='dijkstra'
isActive={this.state.dijkstra}
onClick={this.handleClick}
/>
<Button
name='A* Algorithm'
type='aStar'
isActive={this.state.aStar}
onClick={this.handleClick}
/>
<Button
name='Best First Search'
type='bestSearch'
isActive={this.state.bestSearch}
onClick={this.handleClick}
/>
<Button
name='BFS'
type='bfs'
isActive={this.state.bfs}
onClick={this.handleClick}
/>
<Button
name='DFS'
type='dfs'
isActive={this.state.dfs}
onClick={this.handleClick}
/>
</div>
<div className='info'></div>
<div className='legend'></div>
</div>
);
}
}
the state is used to keep track of which algorithm is active while handleClick changes the state based on the key of the button. The following is the code for the Button component:
export class Button extends React.Component {
constructor(props) {
super(props);
this.state = {
isActive: false,
};
}
componentWillReceiveProps() {
if (this.props.isActive) this.setState({ isActive: true });
}
render() {
let active = "";
if (this.state.isActive) {
active = "active";
}
return (
<button
className={`button ${active}`}
onClick={this.props.onClick(this.props.type)}
>
{this.props.name}
</button>
);
}
}
I receive an error "Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops."
Any help with how I can rectify this or a better way to implement this?
When you do this:
<button
className={`button ${active}`}
onClick={this.props.onClick(this.props.type)}
>
it is going to set onClick to whatever this.props.onClick(this.props.type) returns. So every time it renders, it is going to call this.props.onClick, which is going to call setState, which is going to cause a rerender, which will call it again, etc.
You probably want to do:
onClick={() => this.props.onClick(this.props.type)}

setState() does not change state when called from element that depends on state to render

I have a little component like this (Code below is simplified to the parts needed) that behaves very strange when it comes to updating the state.
class Componenent extends React.Component {
constructor(props) {
super(props);
this.state = {showStuff: false};
}
render() {
return(
//Markup
{this.state.showStuff && (
<button onClick={() => this.setState({showStuff: false})} />
)}
// More Markup
);
}
}
The state gets updated somewhere else in the component, so the prop is true when the button is clicked.
A click also triggers the setState function (callback gets executed), however the state does not update.
My guess is that it does not update because the function is called by an element that directly depends on the state prop to be visible.
I figured out that adding another prop test: true to the state and changing that property to false when the button is clicked also triggers the showStuff prop to change to false. So it works when I make strange hacks.
Can someone explain this weird behavior to me? I can't gasp why the above snippet does not work like intended.
Here is the entire component:
class ElementAdd extends React.Component {
constructor(props) {
super(props);
this.defaultState = {
showElementWheel: false,
test: true
};
this.state = this.defaultState;
}
handleAddCardClick() {
if (this.props.onCardAdd) {
this.props.onCardAdd({
type: ElementTypes.card,
position: this.props.index
});
}
}
handleAddKnowledgeClick() {
if (this.props.onCardAdd) {
this.props.onCardAdd({
type: ElementTypes.knowledge,
position: this.props.index
});
}
}
handleTabPress(e) {
if (e.key === 'Tab') {
e.preventDefault();
let target = null;
if (e.shiftKey) {
if (e.target.previousSibling) {
target = e.target.previousSibling;
} else {
target = e.target.nextSibling;
}
} else {
if (e.target.nextSibling) {
target = e.target.nextSibling;
} else {
target = e.target.previousSibling;
}
}
target.focus();
}
}
hideElementWheel() {
// This is somehow the only option to trigger the showElementWheel
this.setState({ test: false });
}
render() {
return (
<div
className="element-add"
style={{ opacity: this.props.invisible ? 0 : 1 }}
onClick={() => this.setState(prevSate => ({ showElementWheel: !prevSate.showElementWheel }))}
>
<PlusIcon className="element-add__icon" />
{this.state.showElementWheel && (
<React.Fragment>
<div className="element-add__wheel">
<button
autoFocus
className="element-add__circle"
onClick={this.handleAddCardClick.bind(this)}
onKeyDown={this.handleTabPress.bind(this)}
title="New element"
>
<ViewModuleIcon className="element-add__element-icon" />
</button>
<button
className="element-add__circle"
onClick={this.handleAddKnowledgeClick.bind(this)}
onKeyDown={this.handleTabPress.bind(this)}
title="New knowledge-element"
>
<FileIcon className="element-add__element-icon" />
</button>
</div>
<div
className="element-add__close-layer"
onClick={() => {
this.hideElementWheel();
}}
/>
</React.Fragment>
)}
</div>
);
}
}
By writing onClick={this.setState({showStuff: false})} you are actually calling setState as soon as your button is rendered.
You want to give a function reference to onClick, not call it immediately on render.
<button onClick={() => this.setState({showStuff: false})} />
If your button is inside another element with a click listener that you don't want to run on the same click, you must make sure that the click event doesn't propagate to the parent.
<button
onClick={(event) => {
event.stopPropagation();
this.setState({showStuff: false});
}}
/>
Actually the onClick prop expects a function, you are already providing a function call, so the setState will be called each time the component is rendered, not when clicked.
Try this:
<button onClick={() => this.setState({showStuff: false})} />
Should behave as you expect :)
Works perfectly fine when I update showStuff true (see updated code below.). My guess is the code that is supposed to set showStuff: true is not working. I also added some text in the button.
import React from 'react'
import ReactDOM from 'react-dom'
class Componenent extends React.Component {
constructor(props) {
super(props);
this.state = {showStuff: true};
}
render() {
return(
<div>
{this.state.showStuff && (
<button onClick={() => this.setState({showStuff: false})} > This is a button</button>
)}
</div>
);
}
}
ReactDOM.render(<Componenent />,
document.getElementById('root')
);
Before clicking
After clicking

Show and hide a component based on a variable

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>

React components only setting state for one key in constructor function. ES6

I have this component, but it's not setting show in the the state constructor. I can console.log the props and they show the correct params, but for some reason, show is not getting set.
class SubstitutionPanel extends React.Component {
constructor(props) {
super(props);
this.state = {
suggestions: this.props.synonyms_with_levels,
show: this.props.show
}
}
handleToggleShow() {
this.setState({
show: false
})
}
render() {
console.log("sub panel")
console.log(this.state)
console.log(this.props)
if (this.props.synonyms_with_levels.length > 0 && this.state.show) {
return(
<div className="substitution-panel">
<div onClick={() => this.handleToggleShow()} className="glyphicon glyphicon-remove hover-hand"></div>
{this.props.synonyms_with_levels}
</div>
);
} else {
return (
<span>
</span>
);
}
}
}
The parent that renders this child component looks like this:
<SubstitutionPanel synonyms_with_levels= {this.props.synonyms_with_levels} show={this.state.showSubPane} />
I'm really just trying to make a "tooltip" where the parent can open the tooltip.
Is everything ok when you console.log(this.props)?
It may be just a typo here, but in the parent component you have
show={this.state.showSubPane}
and maybe it should be 'showSubPanel' with an L at the end?

Pass a trigger function to two components

Here is what i want to do:
render() {
return (
<div>
<Child onTrigger={xxx} />
<button onClick={xxx} />
</div>
)
}
When button is clicked, I want something to happen in Child. How do I do this? It cannot just be a boolean because it should be a trigger and be called multiple times.
You got two options
1º Add a counter to your parent state and increment it everytime you click the button.
2º Add a ref to your child and trigger the function using this.refs.child.yourFunctionHere()
Something like this
class Parent extends React.Component{
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
this.handleClickWithRef = this.handeClickWithRef.bind(this);
this.state = {
triggerEvent: 0
};
}
handleClick(e) {
this.setState({
triggerEvent: this.state.triggerEvent+1
});
}
handeClickWithRef(e){
this.refs.child.functionA();
}
render() {
return <div>
<Child ref="child" onTrigger={this.state.triggerEvent} />
<button onClick={this.handleClick}>Click to trigger</button>
<button onClick={this.handleClickWithRef}>Click to trigger using ref</button>
</div>;
}
}
class Child extends React.Component{
constructor(props) {
super(props);
}
functionA(){
alert("With ref");
}
componentWillReceiveProps(nextProps){
if(nextProps.onTrigger !== 0 && nextProps.onTrigger !== this.props.onTrigger){
alert("Your event here");
}
}
render(){
return <div>Child Component</div>
}
}
full working example

Categories

Resources