I have below EditAddressPage component which when I click on Manual Entry button, it should send me to the getAddressField method and from there go to <AddressFields /> component. I see when onCLick happens. it is sending me to the getAddressField method but from there it is lost. I was wondering if someone can give me a second though what is missing there because anything I've tried didn't work so far.
import AddressFields from 'components/widget/AddressFields';
import "./EditAddressPage.less";
const EditAddressPage = (props) => {
const getManualAddressEntry = () => {
return (
<AddressFields
{...props}
/>
)
}
return (
<div
<div className="body-container">
<span id="manually-enter-section">
{gt.gettext('Can’t find your address? ${0}',
<Button
color="primary"
iconSide="left"
iconSrc=""
onClick={() => getManualAddressEntry()}
size="medium"
variant="flat"
id="manual-entry-button"
>
Enter it manually
</Button>
)}
</span>
</div>
)
}
export default EditAddressPage;
export default class AddressFields extends PureRenderComponent {
constructor() {
super();
this.x = true;
this.y = undefined;
this.z = undefined;
}
componentWillMount() {
// something
}
componentDidMount() {
// something
}
componentWillUnmount() {
// something
}
render() {}
}
Your getManualAddressEntry() function does not render the component, it just returns it as a value.
To render it, you can use conditional rendering like so:
<Button
color="primary"
iconSide="left"
iconSrc=""
onClick={() => getManualAddressEntry()}
size="medium"
variant="flat"
id="manual-entry-button"
>
Enter it manually
</Button>
{buttonClicked && <AddressFields/>}
buttonClicked (or whatever name you prefer) should be a state that is initially set to false, and getManualAddressEntry() should toggle it between false and true
const [manualAddressOpen, setManualAddressOpen] = useState(false)
const getManualAddressEntry = () => {
setManualAddressOpen(!manualAddressOpen)
}
Example of conditional rendering:
I'm trying to create a modal that asks users if they're an individual or organization, and then shows a sign up modal specific to that type of user. This is what I have so far:
parent:
this.state = {
showInd: false,
showOrg: false,
};
changeInd = () => {
this.setState({
showInd: !this.state.showInd
});
this.props.onClose(); //this closes the initial modal
}
//exact same syntax for changeOrg
render(){
return(
<div onClick={this.changeInd}>
<FontAwesomeIcon icon={faUser} className="fa-7x icon"/>
<span>individual</span>
</div>
<div onClick={this.changeOrg}>
<FontAwesomeIcon icon={faUsers} className="fa-7x icon"/>
<span>organization</span>
</div>
<SignUpInd show={this.state.showInd} />
<SignUpOrg show={this.state.showOrg} />
)}
and the child:
render(){
if (this.props.show){
return(
<various sign up html>
)}
}
The parent component is re-rendering when the state changes, but the child component is not, even though the props are changing. I've tried using componentDidUpdate, but that is also never triggered when the props change here.
What could I be doing wrong?
EDIT: So I've realized that if I comment out the line that closes the initial modal with a callback function, the signUpInd modal will render properly. Why can I not do both?
this works:
import React from "react";
import SignUpInd from "./SignUpInd";
import SignUpOrg from "./SignUpOrg";
import "./styles.css";
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
showInd: false,
showOrg: false
};
}
showInd = () => {
this.setState((state) => ({ showInd: !state.showInd }));
};
showOrg = () => {
this.setState((state) => ({ showOrg: !state.showOrg }));
};
render() {
return (
<React.Fragment>
<div onClick={this.showInd}>
<FontAwesomeIcon icon={faUser} className="fa-7x icon"/>
<span>individual</span>
</div>
<div onClick={this.showOrg}>
<FontAwesomeIcon icon={faUsers} className="fa-7x icon"/>
<span>organization</span>
</div>
<SignUpInd show={this.state.showInd} />
<SignUpOrg show={this.state.showOrg} />
</React.Fragment>
);
}
}
1.At the parent component use a function that changes the state.
state = {
showInd: false,
showOrg: false,
};
stateChange = () =>{
this.setState({showInd:!this.state.showInd})
}
2.Use an onClick function on the div it will give opposite value of what it is right now and pass it as a props to the next component
<div onClick={this.stateChange}> //this onClick just flips showInd to the opposite of what it is currently - that's working properly
<span>individual</span>
</div>
<SignUpInd show={this.state.showInd} stateChange= {this.stateChange} />
3.At the other end just recieve the props and console log it
const {show,stateChange} = this.props
console.log(show);
In my react.js project I have parent and child components (Page like parent and Modal with input like child). I receive data in parent by ajax request and pass it for input from parent to child and fill the child state with it. Also in child component I have 2 buttons: Submit and Cancel. Conside the following code:
**Parent**
render() {
const { projectAux } = this.props;
return (
<ProjectNameFormModal
projectAux={projectAux}
onVisibleChange={e => this.onVisibleProjectNameFormModalChange(e)}
visible={this.state.editProjectNameModalVisible}
/>
)
}
**Child**
import React from 'react';
import {Button, Checkbox, Form, Input, List, Modal, Select} from "antd";
class ProjectNameFormModal extends React.Component{
constructor(props){
super(props);
this.state = {
projectAux: props.projectAux,
visible: props.visible
}
}
shouldComponentUpdate(nextProps, nextState, nextContext) {
if(this.state.projectAux != nextProps.projectAux){
this.setState({
projectAux: nextProps.projectAux,
visible: nextProps.visible
});
}
return true;
}
handleProjectNameInputChange = e => {
let current_state = this.state;
current_state.projectAux[e.target.name] = e.target.value;
this.setState(current_state);
}
handleCancelSubmitProjectNameUpdate = e => {
this.props.onVisibleChange(false);
}
handleSubmitProjectNameUpdate = e => {
console.log(' in handleSubmitProjectNameUpdate');
this.setState({...this.state, visible: false});
this.props.onVisibleChange(false);
}
render() {
return (
<Modal
title='Edit project Name'
visible={this.props.visible}
bodyStyle={{}}//height:"800px"
onSave={{}}
maskClosable={false}
onCancel={this.handleCancelSubmitProjectNameUpdate}
footer={[
<Button key="back" onClick={this.handleCancelSubmitProjectNameUpdate}>
Cancel
</Button>,
<Button key="submit" type="primary" onClick={this.handleSubmitProjectNameUpdate}>
Save
</Button>,
]}
>
<div>
<Input placeholder="ProjectName"
name="name"
onChange={this.handleProjectNameInputChange}
value={this.state.projectAux && (this.state.projectAux.name)}
/>
</div>
</Modal>
);
}
}
export default ProjectNameFormModal;
So the problem is when I enter some new data to input and then press Cancel button, I have also my Parent state updated to the new data which I definetely dont want to happen. When Cancel is pressed, no updates to the parent state should occur but it happens. Btw, parent state does not update when I enter new symbols into input. I tried to use spread operator as it is said here but it did not work.
Any ideas how to fix it would be welcome, thank you.
I am trying to create something similar to React Bootstrap's dropdown component. My starting skeleton is something like the following:
import React from 'react';
const DropDown = props => {
return <div className="dropdown-container">{props.children}</div>;
};
const DropDownToggle = props => {
return <div className="dropdown-toggle">{props.children}</div>;
};
const DropDownContent = props => {
return <div className="dropdown-content">{props.children}</div>;
};
export { DropDown, DropDownToggle, DropDownContent };
These components would be used like this:
<DropDown>
<DropDownToggle>
{/*
The content inside here should be customizable so the user of
these components can specify whatever they want for the toggle
*/}
<button type="button">
my button
</button>
</DropDownToggle>
<DropDownContent>
{/*
The content inside here should be customizable so the user of
these components can specify whatever they want for the content of
the dropdown
*/}
<ContentComponent/>
</DropDownContent>
</DropDown>
Is there a way I can communicate between the two children components (DropDownContent and DropDownToggle)? I have access to the parent component and it just receives and displays the children so far, but I would like to somehow communicate between the children so that the user can click on the toggle to open/close the content. I don't want to use redux.
Thank you in advance!
EDIT
I ended up going with the method that #Train suggested in his/her comment below. I was originally hoping for the ability to nest components manually, but what was most important to me was having the state be self-contained in the parent component. Being able to define the toggle button's HTML as well as the content's HTML was also a requirement. My final implementation allows for both of these things and looks something like this:
import React from 'react';
import PropTypes from 'prop-types';
export class Dropdown extends React.Component {
state = {
isOpen: false,
};
onDropDownToggleClick = () => {
this.setState({ isOpen: !this.state.isOpen });
};
render() {
let contentClasses = 'dropdown-content';
if (this.state.isOpen) {
contentClasses += ' show';
}
return (
<div className="dropdown-container">
<div className="dropdown-toggle" onClick={this.onDropDownToggleClick}>
{this.props.toggle}
</div>
<div className={contentClasses}>{this.props.content}</div>
</div>
);
}
}
Dropdown.propTypes = {
toggle: PropTypes.oneOfType([PropTypes.string, PropTypes.element]).isRequired,
content: PropTypes.oneOfType([PropTypes.string, PropTypes.element])
.isRequired,
};
export default Dropdown;
to use it:
const dropDownToggle = (
<button type="button">
Dropdown
</button>
);
const dropDownContent = 'content';
<DropDown
toggle={dropDownToggle}
content={dropDownContent}
/>
For something like toggling content you can use composition instead of inheritance to pass data around.
From the example of Facebook
This is done with props.children property.
function Dialog(props) {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">
{props.title}
</h1>
<p className="Dialog-message">
{props.message}
</p>
{props.children}
</FancyBorder>
);
}
class SignUpDialog extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.handleSignUp = this.handleSignUp.bind(this);
this.state = {login: ''};
}
render() {
return (
<Dialog title="Mars Exploration Program"
message="How should we refer to you?">
<input value={this.state.login}
onChange={this.handleChange} />
<button onClick={this.handleSignUp}>
Sign Me Up!
</button>
</Dialog>
);
}
handleChange(e) {
this.setState({login: e.target.value});
}
handleSignUp() {
alert(`Welcome aboard, ${this.state.login}!`);
}
}
In the render() I am rendering the Dialog component and passing in the props.
the props are .children and the custom props title, message
This lets us pass child elements directly into the output we can even add components from other classes as I did with the SignUpDialog.
Did you have something like this in mind?
const actionTypes = {
TOGGLE: "TOGGLE"
};
const notRedux = {
actionHandlers: Object.keys(actionTypes).reduce(
(acc, val) => ({ [val]: [], ...acc }),
{}
),
dispatchAction(actionType, data) {
this.actionHandlers[actionType].forEach(handler => handler(data));
},
onAction(actionType, actionHandler) {
this.actionHandlers[actionType].push(actionHandler);
}
};
const DropDown = ({ children }) => {
return <div className="dropdown-container">{children}</div>;
};
const DropDownToggle = () => {
const onClick = () =>
notRedux.dispatchAction(actionTypes.TOGGLE, "oh hi Mark");
return (
<div className="dropdown-toggle">
<button type="button" onClick={onClick}>
my button
</button>
</div>
);
};
const DropDownContent = props => {
notRedux.onAction(actionTypes.TOGGLE, data =>
alert(`DropDownToggle said ${data} //DropDownContent`)
);
return <div className="dropdown-content">{props.children}</div>;
};
const App = () => (
<DropDown>
<DropDownToggle></DropDownToggle>
<DropDownContent>
<span>Content goes here</span>
</DropDownContent>
</DropDown>
);
ReactDOM.render(<App />, document.getElementById("app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app"></app>
In my React component I have a button meant to send some data over AJAX when clicked. I need to happen only the first time, i.e. to disable the button after its first use.
How I'm trying to do this:
var UploadArea = React.createClass({
getInitialState() {
return {
showUploadButton: true
};
},
disableUploadButton(callback) {
this.setState({ showUploadButton: false }, callback);
},
// This was simpler before I started trying everything I could think of
onClickUploadFile() {
if (!this.state.showUploadButton) {
return;
}
this.disableUploadButton(function() {
$.ajax({
[...]
});
});
},
render() {
var uploadButton;
if (this.state.showUploadButton) {
uploadButton = (
<button onClick={this.onClickUploadFile}>Send</button>
);
}
return (
<div>
{uploadButton}
</div>
);
}
});
What I think happens is the state variable showUploadButton not being updated right away, which the React docs says is expected.
How could I enforce the button to get disabled or go away altogether the instant it's being clicked?
The solution is to check the state immediately upon entry to the handler. React guarantees that setState inside interactive events (such as click) is flushed at browser event boundary. Ref: https://github.com/facebook/react/issues/11171#issuecomment-357945371
// In constructor
this.state = {
disabled : false
};
// Handler for on click
handleClick = (event) => {
if (this.state.disabled) {
return;
}
this.setState({disabled: true});
// Send
}
// In render
<button onClick={this.handleClick} disabled={this.state.disabled} ...>
{this.state.disabled ? 'Sending...' : 'Send'}
<button>
What you could do is make the button disabled after is clicked and leave it in the page (not clickable element).
To achieve this you have to add a ref to the button element
<button ref="btn" onClick={this.onClickUploadFile}>Send</button>
and then on the onClickUploadFile function disable the button
this.refs.btn.setAttribute("disabled", "disabled");
You can then style the disabled button accordingly to give some feedback to the user with
.btn:disabled{ /* styles go here */}
If needed make sure to reenable it with
this.refs.btn.removeAttribute("disabled");
Update: the preferred way of handling refs in React is with a function and not a string.
<button
ref={btn => { this.btn = btn; }}
onClick={this.onClickUploadFile}
>Send</button>
this.btn.setAttribute("disabled", "disabled");
this.btn.removeAttribute("disabled");
Update: Using react hooks
import {useRef} from 'react';
let btnRef = useRef();
const onBtnClick = e => {
if(btnRef.current){
btnRef.current.setAttribute("disabled", "disabled");
}
}
<button ref={btnRef} onClick={onBtnClick}>Send</button>
here is a small example using the code you provided
https://jsfiddle.net/69z2wepo/30824/
Tested as working one: http://codepen.io/zvona/pen/KVbVPQ
class UploadArea extends React.Component {
constructor(props) {
super(props)
this.state = {
isButtonDisabled: false
}
}
uploadFile() {
// first set the isButtonDisabled to true
this.setState({
isButtonDisabled: true
});
// then do your thing
}
render() {
return (
<button
type='submit'
onClick={() => this.uploadFile()}
disabled={this.state.isButtonDisabled}>
Upload
</button>
)
}
}
ReactDOM.render(<UploadArea />, document.body);
You can try using React Hooks to set the Component State.
import React, { useState } from 'react';
const Button = () => {
const [double, setDouble] = useState(false);
return (
<button
disabled={double}
onClick={() => {
// doSomething();
setDouble(true);
}}
/>
);
};
export default Button;
Make sure you are using ^16.7.0-alpha.x version or later of react and react-dom.
Hope this helps you!
If you want, just prevent to submit.
How about using lodash.js debounce
Grouping a sudden burst of events (like keystrokes) into a single one.
https://lodash.com/docs/4.17.11#debounce
<Button accessible={true}
onPress={_.debounce(async () => {
await this.props._selectUserTickets(this.props._accountId)
}, 1000)}
></Button>
If you disable the button during onClick, you basically get this. A clean way of doing this would be:
import React, { useState } from 'react';
import Button from '#material-ui/core/Button';
export default function CalmButton(props) {
const [executing, setExecuting] = useState(false);
const {
disabled,
onClick,
...otherProps
} = props;
const onRealClick = async (event) => {
setExecuting(true);
try {
await onClick();
} finally {
setExecuting(false);
}
};
return (
<Button
onClick={onRealClick}
disabled={executing || disabled}
{...otherProps}
/>
)
}
See it in action here: https://codesandbox.io/s/extended-button-that-disabled-itself-during-onclick-execution-mg6z8
We basically extend the Button component with the extra behaviour of being disabled during onClick execution. Steps to do this:
Create local state to capture if we are executing
Extract properties we tamper with (disabled, onClick)
Extend onClick operation with setting the execution state
Render the button with our overridden onClick, and extended disabled
NOTE: You should ensure that the original onClick operation is async aka it is returning a Promise.
By using event.target , you can disabled the clicked button.
Use arrow function when you create and call the function onClick. Don't forget to pass the event in parameter.
See my codePen
Here is the code:
class Buttons extends React.Component{
constructor(props){
super(props)
this.buttons = ['A','B','C','D']
}
disableOnclick = (e) =>{
e.target.disabled = true
}
render(){
return(
<div>
{this.buttons.map((btn,index) => (
<button type='button'
key={index}
onClick={(e)=>this.disableOnclick(e)}
>{btn}</button>
))}
</div>
)}
}
ReactDOM.render(<Buttons />, document.body);
const once = (f, g) => {
let done = false;
return (...args) => {
if (!done) {
done = true;
f(...args);
} else {
g(...args);
}
};
};
const exampleMethod = () => console.log("exampleMethod executed for the first time");
const errorMethod = () => console.log("exampleMethod can be executed only once")
let onlyOnce = once(exampleMethod, errorMethod);
onlyOnce();
onlyOnce();
output
exampleMethod executed for the first time
exampleMethod can be executed only once
You can get the element reference in the onClick callback and setAttribute from there, eg:
<Button
onClick={(e) => {
e.target.setAttribute("disabled", true);
this.handler();
}}
>
Submit
</Button>
Keep it simple and inline:
<button type="submit"
onClick={event => event.currentTarget.disabled = true}>
save
</button>
But! This will also disable the button, when the form calidation failed! So you will not be able to re-submit.
In this case a setter is better.
This fix this set the disabled in the onSubmit of the form:
// state variable if the form is currently submitting
const [submitting, setSubmitting] = useState(false);
// ...
return (
<form onSubmit={e => {
setSubmitting(true); // create a method to modify the element
}}>
<SubmitButton showLoading={submitting}>save</SubmitButton>
</form>
);
And the button would look like this:
import {ReactComponent as IconCog} from '../../img/icon/cog.svg';
import {useEffect, useRef} from "react";
export const SubmitButton = ({children, showLoading}) => {
const submitButton = useRef();
useEffect(() => {
if (showLoading) {
submitButton.current.disabled = true;
} else {
submitButton.current.removeAttribute("disabled");
}
}, [showLoading]);
return (
<button type="submit"
ref={submitButton}>
<main>
<span>{children}</span>
</main>
</button>
);
};
Another approach could be like so:
<button onClick={this.handleClick} disabled={isLoading ? "disabled" :""}>Send</button>
My approach is if event on processing do not execute anything.
class UploadArea extends React.Component {
constructor(props) {
super(props)
this.state = {
onProcess:false
}
}
uploadFile() {
if (!this.state.onProcess){
this.setState({
onProcess: true
});
// then do your thing
this.setState({
onProcess: false;
});
}
}
render() {
return (
<button
type='submit'
onClick={() => this.uploadFile()}>
Upload
</button>
)
}
}
ReactDOM.render(<UploadArea />, document.body);
Try with this code:
class Form extends React.Component {
constructor() {
this.state = {
disabled: false,
};
}
handleClick() {
this.setState({
disabled: true,
});
if (this.state.disabled) {
return;
}
setTimeout(() => this.setState({ disabled: false }), 2000);
}
render() {
return (
<button type="submit" onClick={() => this.handleClick()} disabled={this.state.disabled}>
Submit
</button>
);
}
}
ReactDOM.render(<Form />, document.getElementById('root'));