Callback to control modal display - javascript

I have built a modal to display login/register modal. By default, the modal is opened by another component using the props show. This working when the modal is called by this component.
Also the modal Form is called from my Header.js as shown below:
<LoginRegisterForm displayPopUp={this.state.showLogin} onHide={() => this.setState({ showLogin: false })}/>}
In this case, the state showLogin is set to true when clicking on the Login/Register, the <LoginRegisterform is now showing the modal because displayPopup props is set to true
The code is below:
Form.js
const Form = ({ initialState = STATE_SIGN_UP, displayPopUp}) => {
const [mode, toggleMode] = useToggle(initialState);
const [display, toggleDisplay] = useToggleDisplay(displayPopUp);
console.log('----------------------------------------------------------')
console.log('displayPopUp: ' + displayPopUp)
console.log('display: ' + display)
console.log('toggleDisplay: ' + toggleDisplay)
console.log('----------------------------------------------------------')
return (
<Modal className="modal" show={displayPopUp} size="lg">
<Container pose={mode === STATE_LOG_IN ? "signup" : "login"}>
<div className="container__form container__form--one">
<FormLogin mode={mode} toggleDisplay={toggleDisplay} />
</div>
<div className="container__form container__form--two">
<FormSignup mode={mode} toggleDisplay={toggleDisplay}/>
</div>
<Overlay toggleMode={toggleMode} mode={mode} />
</Container>
</Modal>
);
};
in the FormLogin, I do have a Cancel button which allow me to close the modal located in the Form.js when needed. However, I do not know how I can make the modal close by change the show params in the Form.js when the close control is in the class FormLogin
FormLogin.js
import React from 'react'
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome'
import SocialButton from './styled/SocialButton'
import SlidingForm from './styled/SlidingForm'
import WhiteButton from '../../materialdesign/WhiteButton'
import { faFacebook, faGoogle, faLinkedinIn } from '#fortawesome/free-brands-svg-icons'
import Auth from '../../../data/network/Auth';
import Constant from '../../../config/Constant';
import CancelIcon from '#material-ui/icons/Cancel';
class FormLogin extends React.Component {
constructor(props, context) {
super(props);
this.state = {
email: '',
password: '',
loading: false,
error: '',
toggleDisplay: this.props.toggleDisplay
};
}
requestSignIn = async (event) => {
event.preventDefault();
this.setState({loading: true})
try {
const authData = await Auth.getToken(`${this.state.email}`, `${this.state.password}`);
sessionStorage.setItem(Constant.ALL, authData)
sessionStorage.setItem(Constant.AUTH_TOKEN, authData.token)
sessionStorage.setItem(Constant.DISPLAY_NAME, authData.user_display_name)
sessionStorage.setItem(Constant.EMAIL, authData.user_email)
sessionStorage.setItem(Constant.NICENAME, authData.user_nicename)
window.open("/", "_self") //to open new page
this.setState({loading: false })
this.close()
} catch (error) {
console.warn("Connection to WP - Auth Token failed ")
console.error(error);
}
}
requestForgotPassword = () => {
}
handleOnChange = (event) => {
this.setState({[event.target.name]: event.target.value})
}
render(){
const { email, password } = this.state;
return(
<SlidingForm>
<div style={{textAlign:"left"}}>
<CancelIcon style={{ color: "#ff7255" }} onClick={() => this.state.toggleDisplay(false) }/>
</div>
<h1 style={titleStyle}>Sign in</h1>
<div style={{textAlign: "center"}}>
<SocialButton>
<FontAwesomeIcon icon={faFacebook} />
</SocialButton>
<SocialButton>
<FontAwesomeIcon icon={faGoogle} />
</SocialButton>
<SocialButton>
<FontAwesomeIcon icon={faLinkedinIn} />
</SocialButton>
</div>
<p style={txtStyle}>or use your account</p>
<form style={{textAlign: "center"}}>
<input style={formStyle} placeholder="Email" type="text" name="email" value={ email } onChange={ this.handleOnChange }/>
<input style={formStyle} placeholder="Password" type="password" name="password" value={ password } onChange={ this.handleOnChange } />
</form>
<p style={txtSpan}>
<a href="#" onClick={this.requestForgotPassword}>Forgot your password?</a>
</p>
<div style={{textAlign: "center", marginTop: "15px"}}>
<WhiteButton text="Sign in" onClick={this.requestSignIn}></WhiteButton>
</div>
</SlidingForm>
);
}
}
export default FormLogin
For now I was doing this :
<CancelIcon style={{ color: "#ff7255" }} onClick={() => this.state.toggleDisplay(false)
but it's not working, it's seems not having control on the Form.js.
toggleDisplay code is below:
import { useState } from 'react'
export const STATE_SHOW = true
export const STATE_HIDE = false
const useToggleDisplay = initialDisplayState => {
const [display, setDisplay] = useState(initialDisplayState)
const toggleDisplay = () =>
setDisplay(display === false ? true : false)
console.log('-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%')
console.log('display: ' + display)
console.log('toggleDisplay: ' + toggleDisplay)
console.log('-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%')
return [display, toggleDisplay]
}
export default useToggleDisplay
The Overall logic:
Modal is called from Header.js and show is set to false by default and switch to true when clicking on the menu option login
The Modal Form.js is handling login and register screen
What is the best option to be able to set show to false in the Form.js when the close is triggered in the FormLogin.js ?
Thanks

Instead of assigning the toggleDisplay prop to local state, just invoke the prop directly. This should work for updating the <Form />'s display state to false.
Also, do you intend for the <CancelIcon /> to toggle the modal open/close state, or is it just to close the modal? If it's the latter, you may want to update the prop name to closeModal instead of toggleDisplay.
<div style={{textAlign:"left"}}>
<CancelIcon style={{ color: "#ff7255" }} onClick={() => this.props.toggleDisplay(false)
}/>
</div>
Your useToggleDisplay func is confusing, and the original version was not accepting any arguments for toggleDisplay, hence even though you passed in false, it did not update the display state. I've removed useToggleDisplay since it doesn't do anything special.
const [display, setDisplay] = useState(initialDisplayState)
I also realised that <Modal /> is accepting displayPopUp instead of display. If you use displayPopUp, it doesn't know that display has been set to false, and therefore, remain open. And I passed setDisplay setter to the <FormLogin /> components.
<Modal className="modal" show={display} size="lg">
<Container pose={mode === STATE_LOG_IN ? "signup" : "login"}>
<div className="container__form container__form--one">
<FormLogin mode={mode} toggleDisplay={setDisplay} />
</div>
<div className="container__form container__form--two">
<FormSignup mode={mode} toggleDisplay={setDisplay}/>
</div>
<Overlay toggleMode={toggleMode} mode={mode} />
</Container>
</Modal>

Related

React - how to pass data into Modal (bootstrap)

I'm trying to pass data into Modal (bootstrap) popup and display some data.
I have a list of orders with a button 'display info', and every button that i press should display on the popup (Modal) diffrent data.
My question is how should i pass the data to the Modal?
this line <Button variant="primary" onClick={() => {this.handleModal(index)}}> Items info</Button> should trigger the Modal. In the handleModal function it passes the order index. And then i update the index on the setState of the handleModal function.
The Modal open but nothing passes to it.
I'm not sure that this is the correct way of doing it.
Also the Modal is inside the loop of the filteredOrders, should i move the Modal outside the loop?
And if yes, how should i do that and where?
import React, {useState} from 'react';
import './App.scss';
import {createApiClient, Item, Order} from './api';
import Modal from 'react-bootstrap/Modal';
import Button from 'react-bootstrap/Button';
import 'bootstrap/dist/css/bootstrap.min.css'
export type AppState = {
orders?: Order[],
search: string;
show:boolean;
item?: Item,
order_id: number,
}
const api = createApiClient();
export class App extends React.PureComponent<{}, AppState> {
state: AppState = {
search: '',
show:false,
order_id: 0,
};
searchDebounce: any = null;
async componentDidMount() {
this.setState({
orders: await api.getOrders()
});
}
async getItem(itemID: string){
this.setState({
item: await api.getItem(itemID)
});
}
render() {
const {orders} = this.state;
return (
<main>
<h1>Orders</h1>
<header>
<input type="search" placeholder="Search" onChange={(e) => this.onSearch(e.target.value)}/>
</header>
{orders ? <div className='results'>Showing {orders.length} results</div> : null}
{orders ? this.renderOrders(orders) : <h2>Loading...</h2>}
</main>
)
}
handleModal(index: number)
{
this.setState({
show:true,
order_id: index,
})
}
handleClose () {
this.setState({show: false})
}
renderOrders = (orders: Order[]) => {
const filteredOrders = orders
.filter((order) => (order.customer.name.toLowerCase() + order.id).includes(this.state.search.toLowerCase()));
const requiredItem = this.state.order_id;
const modelData = filteredOrders[requiredItem];
return (
<div className='orders'>
{filteredOrders.map((order,index) => (
<div className={'orderCard'}>
<div className={'generalData'}>
<h6>{order.id}</h6>
<h4>{order.customer.name}</h4>
<h5>Order Placed: {new Date(order.createdDate).toLocaleDateString()}</h5>
</div>
<div className={'fulfillmentData'}>
<h4>{order.itemQuantity} Items</h4>
<img src={App.getAssetByStatus(order.fulfillmentStatus)}/>
{order.fulfillmentStatus !== 'canceled' &&
<a href="#" onClick={() => this.ChangeStatus(order)}>Mark
as {order.fulfillmentStatus === 'fulfilled' ? 'Not Delivered' : 'Delivered'}</a>
}
</div>
<div className={'extraData'}>
<Button variant="primary" onClick={() => {this.handleModal(index)}}> Items info</Button>
<Modal show={this.state.show} >
{/*{console.log(modelData)}*/}
{/*<Modal.Header closeButton>*/}
{/* <Modal.Title>Item Info</Modal.Title>*/}
{/*</Modal.Header>*/}
<Modal.Body>
{ console.log(modaelData) }
</Modal.Body>
<Modal.Footer>
<Button onClick={() =>{ this.handleClose()}}>
Close
</Button>
</Modal.Footer>
</Modal>
</div>
<div className={'paymentData'}>
<h4>{order.price.formattedTotalPrice}</h4>
<img src={App.getAssetByStatus(order.billingInfo.status)}/>
</div>
</div>
))}
</div>
)
};
}
export default App;
I don't think you need to pass data to the Modal, but rather compose the Modal with the data in the first place. It is currently empty. Then you can continue to hide/show the complete Modal with handleModal.

How to disable login button on a dynamically rendered form

I am working on a login form where each input is created dynamically as a field.
This is my Login.js file:
import _ from 'lodash';
import React, { Component } from 'react';
import {reduxForm, Field } from 'redux-form';
import{ Link } from 'react-router-dom';
import FIELDS from './loginFields';
import LoginField from './LoginField'
import { connect } from 'react-redux';
import * as actions from '../../actions'
class LoginForm extends Component {
constructor(){
super();
this.state={
username: '',
password: ''
};
};
handleChange = (e)=>{
this.setState({username: e.target.value, password: e.target.value});
};
renderFields(){
return _.map(FIELDS, ({ label, name, type })=> {
return <Field onChange={this.handleChange} className='purple-text' key={name} component={ LoginField } type={type} label={label} name={name} />
});
};
render(){
const { username, password } = this.state;
const isEnabled = username.length > 0 && password.lenth>7;
return (
<div className='valign-wrapper row login-box' style={{marginTop:'100px'}}>
<div className='col card hoverable s10 pull-s1 m6 pull-m3 l4 pull-l4'>
<form method='POST' action='/api/login'>
<div className = 'card-content'>
<span className='card-title purple-text' style={{textAlign:'center'}}>Login<a href='/register'> Not a member? sign up!</a></span>
<div className='center-align row'>
<li key='google' style={{marginLeft: '30px'}} className='col m6 center-align white-text darken-3'><a className='white-text' href='/auth/google'><img alt="" src="https://img.icons8.com/cute-clipart/64/000000/google-logo.png"/></a></li>
<li key='facebook' className='col center-align white-text darken-3'><a className='white-text' href='/auth/facebook'><img alt = "" src="https://img.icons8.com/cute-clipart/64/000000/facebook-new.png"/></a></li>
</div>
<div className='row input-field col s12'>
{this.renderFields()}
<Link to='/' className='purple btn-flat left white-text'>Back</Link>
<button disabled={!isEnabled} type='submit' className='purple btn-flat right white-text'>Login
<i className='material-icons right'>done</i>
</button>
</div>
</div>
</form>
</div>
</div>
);
};
};
function validate(values){
const errors = {};
_.each(FIELDS, ({name})=>{
if(!values[name]){
errors[name] = 'You must enter a value!'
}
});
return errors;
};
const form = reduxForm({
validate,
form: 'LoginForm'
});
export default connect(null, actions)(form(LoginForm));
Here is loginFields.js
export default
[
{ label: 'Username', name: 'username', type: 'text'},
{ label: 'Password', name: 'password', type: 'password'}
];
and here is LoginField.js
import React from 'react';
export default ({ input, label, type, meta })=>{
return(
<div>
<label className='purple-text'>{label}</label>
<input {...input} type={type} style= {{marginBottom: '5px'}}/>
<div className = "red-text" style={{ marginBottom: '20px'}}>
{meta.touched && meta.error}
</div>
</div>
);
};
I am having trouble properly setting onChange and my constructor to disable the login button until all fields are filled. I have been able to disable the button until a single input has started to be filled in, not disabled at all, and not enabled at all. but have not been able to achieve the desired outcome.
I have tried using lodash to map over each field grabbing values by the input name property, and moving functions around.
Any help would be greatly appreciated, if i can provide any more information for this question please let me know.
The initial problem I see is the onChange function will update state for both password and username whenever either of them is changed. The function takes the event and does not distinguish as to which input is the target. You can pass an additional parameter from the Field that includes the field name, or you can check the target's id or something so you know which input's state should be updated.
In LoginForm
handleChange = (e, name)=>{
this.setState({[name]: e.target.value});
};
You also need to pass the onChange callback down to the actual input in LoginField.js
import React from 'react';
export default ({ name, label, type, meta, onChange, ...props })=>{
return(
<div>
<label className='purple-text'>{label}</label>
<input onChange={(e) => onChange(e, name)} {...props} type={type} style= {{marginBottom: '5px'}}/>
<div className = "red-text" style={{ marginBottom: '20px'}}>
{meta.touched && meta.error}
</div>
</div>
);
};
Here's a codeSandbox.
just adding this as an answer in case anyone else comes across this issue.
after tons of digging I finally found documentation. in redux form has a built in prop called {invalid} which checks against the validate function. instead of messing with state all i had to do was add
const {invalid} = this.props;
inside the render method. constructor and handle change and onChange were no longer necessary.. then.
<button disabled={invalid}>

Type Error property undefined even though action fired off defining it

I am currently having a button redirect to the same location a button on a prior dashboard takes you to it is essentially a render of a graph and all of its notes and questions rendered. Now the specific snippet in the error works from my initial location which is denoted below in my screenshot that speculation button will take you to the rendered show route below it. However, the button that appears after you have made a new question and note do not they use the same snippet but one errors out I am also adding the snippet for the buttons.
*edit 1 adding show.js
show.js
// built-in(lifecycle) methods imported here
import React, { Component } from 'react'
// import components from local
import Graph from '../components/Graph'
import Notes from '../components/Notes'
import Questions from '../components/Questions'
//imbrl allows us to enable routing by updating url and rendering needed component listed in routeer
import { NavLink } from 'react-router-dom'
//bootstrap WIP
import Button from 'react-bootstrap/Button'
import Row from 'react-bootstrap/Row'
import Col from 'react-bootstrap/Col'
import Card from 'react-bootstrap/Card'
// access state from redux store
import { connect } from 'react-redux'
class Show extends Component {
render() {
// variables for objects
const graph = this.props.graphs.find(graph => { return graph.id === parseInt(this.props.match.params.id)})
const notes = this.props.notes.filter(note => note.graph.id === graph.id)
const questions = this.props.questions.filter(question => question.graph.id === graph.id)
// if graph exists it loads all corresponding notes and questions with it
if (graph) {
return (
<Row>
<Col md={3}>
<Notes graph={graph} notes={notes} />
</Col>
<Col md={6} >
<Card>
<Graph graph={graph}/>
</Card>
<NavLink
to={`/graphs/${graph.id}/interact`}>
<Button>Interact</Button>
</NavLink>
</Col>
<Col md={3}>
<Questions graph={graph} questions={questions} />
</Col>
</Row>
)
} else {
return (
<div>
<NavLink
style={{marginRight: '10px'}}
to="/">
<Button variant="dark" size="lg" block>Add Data to get started</Button>
</NavLink>
</div>
)
}
}
}
// this will need access to the objects
const mapStateToProps = state => {
return {
graphs: state.graphs,
notes: state.notes,
questions: state.questions
}
}
export default connect (mapStateToProps)(Show)
graphinput.js
import React, { Component } from 'react'
// import actions future?
import { addNote } from '../actions/addSpeculations'
import { addQuestion} from '../actions/addSpeculations'
// browser url routing render component as needed
import { NavLink } from 'react-router-dom'
// import local components
import Note from '../components/Note'
import Question from '../components/Question'
// bootstrap styling
import Button from 'react-bootstrap/Button'
import Form from 'react-bootstrap/Form'
// allows access to redux store this is a stateful component
import { connect } from 'react-redux'
class GraphInput extends Component {
// initial state placejolders
state ={
note: {
content: ""
},
question: {
content: ""
},
visible: false,
view: false
}
// state will be updated everytime the form value changes
handleChange = (event) => {
this.setState({
[event.target.name]: {content: event.target.value, graph_id: this.props.graph_id}
})
}
// visible key will show as true to confirm before calling fetch.
handleSubmit = (event) => {
event.preventDefault()
this.setState({visible: true})
}
handleSave = () => {
this.props.addNote(this.state.note)
this.props.addQuestion(this.state.question)
this.setState({
note: {content: ""},
question: {content: ""},
visible: false,
view: true
})
}
// if user cancels submission, state should reset to initial values
handleCancel = () => {
this.setState({
note: {content: ""},
question: {content: ""},
visible: false,
view: false
})
}
render () {
// Need to check current state to base what return should be used for what user interaction has done.
/*const validated= this.state.note.content.length > 20 && this.state.note.content.length > 20*/
if (this.state.visible === false && this.state.view === false){
/// render form and graph
return (
<div>
<h3> Add your Observations or Speculation below. </h3>
<Form onSubmit={event => this.handleSubmit(event)} >
<Form.Group>
<Form.Control size="lg" type="text" name="note" placeholder="Make an observation." value={this.state.note.content} onChange={event => this.handleChange(event)} />
</Form.Group>
<Form.Group>
<Form.Control size="lg" type="text" name="question" placeholder="Ask a question" value={this.state.question.content} onChange={event => this.handleChange(event)} />
</Form.Group>
<Button type="submit" >Add</Button>
</Form>
</div>
)
} else if (this.state.visible === true && this.state.view === false) {
/// rwendering draft to confirm submission
return (
<div>
<Note note={this.state.note} />
<Question question={this.state.question} />
<Button type="submit" onClick={this.handleSave}> Save Speculation to Database</Button>
<Button type="submit" variant="danger" onClick={this.handleCancel}>Cancel</Button>
</div>
)
} else if (this.state.view === true) {
// after saving, user can now navigate to view all
return (
<NavLink
style={{ marginRight: '10px'}, {backgroundColor: 'transparent'}}
to={`/graphs/${this.props.graph_id}/speculations`}
graph={this.props.graph}>
<Button size="lg" block>View All Speculation for This Graph</Button>
</NavLink>
)
}
}
}
// only state needed is graph speculations for specific graph
const mapStateToProps = (state) => {
return {
graph: state.graph
}
}
// Dispatch to props for Notes and Questions
export default connect(mapStateToProps, {addNote, addQuestion})(GraphInput)
Broken Button
<NavLink
style={{ marginRight: '10px'}, {backgroundColor: 'transparent'}}
to={`/graphs/${this.props.graph_id}/speculations`}
graph={this.props.graph}>
<Button size="lg" block>View All Speculation for This Graph</Button>
</NavLink>
Working Button
<NavLink
style={{ marginRight: '10px'}}
to={`/graphs/${this.props.graph.id}/speculations`}
url={this.props.graph.screenshot_url} >
<Button variant="success" >
Speculations
</Button>
</NavLink>
This is the button that works
This is where the button should take you
This button does not go to the show with that specific graph's notes and questions. (Both are using the same snippet to identify graph and its id or are supposed to)

React Modals: Modal component only opens the first-clicked modal and none else

I am new to StackOverflow so pardon me for bad wording of the problem.
I am learning React currently and as a small project, I am creating a course-search app which will filter the courses on the basis of input entered by the user from a JSON file and display them.
I have added modals to each card component which should open on the click of a button within the card.
The problem I'm facing is that when I click the button, it only opens the modal first clicked one, and none else.
Here is the code:
import MyModal from '../Modal/Modal'
import courseList from "./courses.json";
class App extends Component {
state = {
search: "",
modal: false,
};
selectModal = (id) => {
this.setState({
modal: {
[id]: !this.state.modal
}
})
}
rendercourse = course => {
var dep = course.dep;
return (
<div className="col-md-3" style={{ marginTop: "20px" }}>
<Card className="card">
<CardBody className={dep}>
<CardTitle title={course.id}>
{course.code}
</CardTitle>
<CardText className="title">{course.name}</CardText>
<p className="ic">{course.ic}</p>
Units: {course.unit}<br />
<button onClick= {
this.selectModal.bind(this, course.code)}>More Info</button>
</CardBody>
<MyModal
displayModal={this.state.modal[course.code]}
coursename={course.name}
coursecode={course.code}
courseic={course.ic}
coursedep={course.dep}
courseunit={course.unit}
closeModal={this.selectModal} />
</Card>
</div>
);
};
onchange = e => {
this.setState({ search: e.target.value });
};
render() {
const { search } = this.state;
const filteredcourses = courseList.filter(course => {
return course.name.toLowerCase().indexOf(search.toLowerCase()) !== -1;
});
return (
<div className="flyout">
<main style={{ marginTop: "4rem" }}>
<div className="container">
<div className="row">
<div className="col-12">
<center>
<h3>
Search for a course
</h3>
</center>
</div>
<div className="col">
<Input
label="Enter the name of the course"
icon="search" className="in"
onChange={this.onchange}
/>
</div>
<div className="col" />
</div>
<div className="row">
{filteredcourses.map(course => {
return this.rendercourse(course);
})}
</div>
</div>
</main>
</div>
);
}
}
export default App;
And here is the modal component:
const MyModal = props => {
function displayInfo () {
return (
<div>
<div>{props.coursename}</div>
<div>{props.courseic}</div>
<div>{props.courseunit}</div>
<div>{props.coursedep}</div>
<div>{props.coursecode}</div>
</div>
);
}
function closeModal (e) {
e.stopPropagation()
props.closeModal()
}
let modal = (
<div
className="modal"
onClick={ closeModal }>
<div className="modal-content"
onClick={ e => e.stopPropagation() }>
<span
className="close"
onClick={ closeModal }>×
</span>
<div className="modal-flex">
{displayInfo()}
</div>
</div>
</div>
)
return ( props.displayModal ? modal : null);
}
export default MyModal;
I want the card-specific modal to open up whenever the button is clicked.
Your selectModal function doesn't flip the state of each modal, but only that of the first one.
If any modal is defined in the state (be it the first or any other modal), this.state.modal will evaluate to true. (an object with at least some content is true)
To allow all your modals to be opened at the same time simply flip the value of the modal element in your modal state object.
this.state.modal[id] is undefined by default, which will evaluate to boolean false.
selectModal = (id) => {
this.setState({
modal: {
[id]: !this.state.modal[id]
}
})
}
If you'd rather only open one modal at a time, you should simply store the id of your modal in the state instead of tracking all modals in there:
selectModal = (id) => {
this.setState({
modal: id
})
}
To close a modal change your closeModal prop to pass undefined/false instead of the modal ID and change your modal display prop so it will perform the check whether your modal state and the coursecode match:
this.state.modal === course.code

Enable or disable a button based on a TextField value in React.js

I am making a form like
I want the add button to be active whenever user is changing the "Tags" input text.
I am using material-ui and I made a Input component.
const SingleInput = (props) => (
<Fragment>
<FormControl margin="normal" required fullWidth>
<TextField
id={props.id}
type={props.type}
name={props.name}
label={props.label}
value={props.content}
variant={props.variant}
placeholder ={props.placeholder}
onChange={props.controlFunc}>
</TextField>
</FormControl>
</Fragment>
);
export default SingleInput;
and I import this into my form and like:
class AddCompanyForm extends React.Component {
constructor() {
super();
this.state = {
company_name: "",
company_description: "",
company_tag: "",
company_tags: "",
company_contact: "",
disabled: true
};
this.handleOnSubmit = this.handleOnSubmit.bind(this);
this.handleOnChange = this.handleOnChange.bind(this);
this.handleOnSelect = this.handleOnSelect.bind(this);
}
handleOnChange(e) {
e.preventDefault();
this.setState({ [e.target.name]: e.target.value });
this.setState({ disabled: false });
console.log("teg", this.state.company_tag.length);
console.log("cont", this.state.company_contact.length);
if (this.state.company_tag.length == 1) {
this.setState({ disabled: true });
}
}
handleOnSubmit(e) {
e.preventDefault();
this.props.createCompany(this.state);
}
handleOnSelect(e) {
e.preventDefault();
chipsValue.push(this.state.company_tag);
this.setState({
company_tags: chipsValue,
company_tag: "",
disabled: true
});
}
render() {
return (
<Paper style={styles.paper}>
<Avatar>
<LockIcon />
</Avatar>
<Typography variant="headline">Add a New Company</Typography>
<form onSubmit={this.handleOnSubmit}>
<SingleInput
id={"company_name"}
type={"company_name"}
name={"company_name"}
label={"Company Name"}
content={this.state.company_name}
controlFunc={this.handleOnChange}
variant={"filled"}
/>
<SingleInput
id={"company_description"}
type={"company_description"}
name={"company_description"}
label={"Description"}
content={this.state.company_description}
controlFunc={this.handleOnChange}
variant={"filled"}
/>
<SingleInput
id={"company_tags"}
type={"company_tags"}
name={"company_tag"}
label={"Tags (to add dropdown here!)"}
content={this.state.company_tag}
controlFunc={this.handleOnChange}
variant={"filled"}
/>
<Button
disabled={this.state.disabled}
onClick={this.handleOnSelect}
variant="raised"
color="secondary"
>
Add
</Button>
<SingleInput
id={"company_contact"}
type={"company_contact"}
name={"company_contact"}
label={"Contact"}
content={this.state.company_contact}
controlFunc={this.handleOnChange}
variant={"filled"}
/>
<Button type="submit" fullWidth variant="raised" color="primary">
Add Company
</Button>
</form>
</Paper>
);
}
}
const mapDispatchToProps = dispatch =>
bindActionCreators(
{
createCompany
},
dispatch
);
export default connect(
null,
mapDispatchToProps
)(AddCompanyForm);
Now the problem is even when I change "Company Name" or any other input button, the Add button becomes enabled.
Any help is very much appreciated.
Check the example below using React Hooks for the button state, and the onChange property of the TextField to set it.
export default function TextFieldAndButton (props) {
const [btnDisabled, setBtnDisabled] = useState(true)
return (
<>
<TextField
onChange={(text) => setBtnDisabled(!text.target.value)}
/>
<Button disabled={btnDisabled}>OK</Button>
</>
)
}
The problem here is that setState is async and you are using state values inside handleOnChange before it is updated. Either use setState callback to calculate disable or a better way is to calc disabled in render. This approach makes it much simpler and even works while rendering for the first time.
render() {
const disabled = !this.state.company_tag.length;
return (
// ...
<Button
disabled={disabled}
onClick={this.handleOnSelect}
variant="raised"
color="secondary"
>
Add
</Button>
// ...
);
}

Categories