Rapid onChange causes OnClick not to register - reactjs

I set the state when I onChange of a input field and then have a submit function on the onClick. The onClick doesn't register at all if I click it within a second or so of the last input.
I have cut everything out of the component that I don't need and am left this:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import Loader from '../utils/Loader';
import './CustomMenu.scss';
import {custom_getCategory} from '../../actions/customActions';
import propsDebug from '../utils/propsDebug';
class CustomMenu extends Component {
constructor(props) {
super(props);
this.state = {
input: "",
customWords: [],
}
this.formToState = this.formToState.bind(this);
this.submit = this.submit.bind(this);
}
formToState(e) {
const {value, name} = e.target;
this.setState({[name]: value});
}
submit() {
const {input} = this.state;
this.setState( state => {
state.input = "";
console.log("newState", state);
return state;
});
}
render() {
const {input, customWords} = this.state;
return (
<div className="CustomMenu">
<input name="input" value={input} onChange={(e) => this.formToState(e)}/>
<button onClick={(e) => {
console.log("onClick");
this.submit(e);
}} style={{margin: "10px"}}>Add</button>
</div>
)
}
}
const mapStateToProps = (state, ownProps) => ({
data: state[ownProps.reduxState],
custom: state.custom,
})
export default connect(mapStateToProps, {custom_getCategory})(CustomMenu);
Any ideas how I can fix this? I feel I have used this pattern many times before without issues - I don't know what I am missing.

<button onClick={(e) => {
console.log("onClick");
this.submit(e);
}} style={{margin: "10px"}}>Add</button>
I think the bug is from the callback you passed to your onClick(). You need to return the this.submit() before you can get it to fire.

<button onClick={(e) => (
console.log("onClick");
this.submit(e);
)} style={{margin: "10px"}}>Add</button>
use an implicit return instead.

Related

How to transform this function component into a class component? (React.js)

I am new to React and struggle to transform this function component into a class component with a constructor(). I can't figure out how to transform the functions happening onSubmit and onClick.
Thank you very much.
The function component:
import React, { useState, Component } from 'react';
import { render } from 'react-dom';
import './Options.css';
const Test = (props) => {
const [links, setLinks] = useState([]);
const [link, setLink] = useState('');
function handleSubmit(e) {
e.preventDefault();
const newLink = {
id: new Date().getTime(),
text: link,
};
setLinks([...links].concat(newLink));
setLink('');
}
function deleteLink(id) {
const updatedLinks = [...links].filter((link) => link.id !== id);
setLinks(updatedLinks);
}
return (
<div className="OptionsContainer">
<form onSubmit={handleSubmit}>
<input
type="text"
onChange={(e) => setLink(e.target.value)}
value={link}
/>
<button type="submit"> + </button>
</form>
{links.map((link) => (
<div key={link.id}>
<div>{link.text}</div>
<button onClick={() => deleteLink(link.id)}>Remove</button>
</div>
))}
</div>
);
};
export default Test;
When we work with class components a couple of things got changed, we have a new object this.state, a new function this.setState, you have to bind your functions on the constructor if you want to have access to this inside it. I think you'll learn way more if you read the code, so here this is your component as a class component:
import React, { Component } from 'react';
import './Options.css';
class App extends Component {
constructor() {
super()
this.state = {
links: [],
link: '',
}
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(e) {
e.preventDefault();
const newLink = {
id: new Date().getTime(),
text: this.state.link,
};
this.setState({links: [...this.state.links, newLink], link: ''});
}
deleteLink(id) {
const updatedLinks = [...this.state.links].filter((link) => link.id !== id);
this.setState({...this.state, links: updatedLinks });
}
render() {
console.log(this.state)
return (
<div className="OptionsContainer">
<form onSubmit={this.handleSubmit}>
<input
type="text"
onChange={(e) => this.setState({ ...this.state, link: e.target.value})}
value={this.state.link}
/>
<button type="submit"> + </button>
</form>
{this.state.links.map((link, index) => (
<div key={link.id}>
<span>{link.text}</span>
<button onClick={() => this.deleteLink(link.id)}>Remove</button>
</div>
))}
</div>
);
}
};
export default Test;
Without seeing your original functional component, it's going to be hard to tell you what the class component should look like.
To answer your question about why onSubmit might not be working, it's because onSubmit is expecting to be passed an uncalled function, so that the <form> element can call that function itself when the time is right.
You are currently calling the function this.handleOnSubmit(), which returns nothing, so no handler for the form submit is properly assigned. Try removing the call to the function, like this:
<form onSubmit={this.handleSubmit}>

duplicates being created in todo list app

I am creating a todo list where when the user clicks the checkbox "complete" that is next to the todo item, it appears in the complete component however there is a duplicate of that item that is being added as well and i am also having an issue trying to have the checkbox not appear in the completed component...
When a user creates a new todo it appears in the active component first and it has a checkbox next to it called completed and when the user clicks the checkbox it appears in the completed component
import React from 'react';
import Active from './Components/Active';
import Completed from './Components/Completed';
import Todoform from './Components/Todoform';
import './App.css';
class App extends React.Component {
state = {
items: [],
task: '',
id: 0,
completedItems: []
}
handleInput = (event) => {
this.setState({
task: event.target.value
})
}
handleSubmit = (event) => {
event.preventDefault()
const newTask = {
id: this.state.id,
title: this.state.task
}
const updatedItems = [...this.state.items, newTask]
this.setState({
items: updatedItems,
task: '',
id: this.state.id + 1
})
}
handleComplete = (newTask) => {
this.setState({completedItems: [...this.state.items, newTask]})
//console.log(this.state.items)
}
render() {
return (
<div id="main-content">
<h1>Task Lister</h1>
<Todoform
handleChange={this.handleInput}
handleSubmit={this.handleSubmit}
task={this.state.task}
/>
<Active
items={this.state.items}
handleComplete={this.handleComplete}
/>
<Completed
completedItems={this.state.completedItems}
/>
</div>
)
}
}
export default App;
import React from 'react'
class Todo extends React.Component{
state = {
checked: false
}
handleCheck = () => {
this.setState({
checked: !this.state.checked
})
}
handleClick = () => {
this.props.handlecompletedList(this.props.title)
}
render(){
const { title } = this.props
return (
<div className="ui checked checkbox">
<input type="checkbox" checked={this.state.checked} onChange={this.handleCheck}
onClick={this.handleClick}/>
<label>Completed {title}</label>
</div>
)
}
}
export default Todo;
import React from 'react'
import Todo from './Todo'
const Active = (props) => {
const { items, handleComplete } = props
return(
<div id="activeList">
<h2 className="position">Active</h2>
<ul id="tasks">
{
items.map(item => {
return(
<Todo key={item.id} handlecompletedList={handleComplete} title={item.title}/>
)
})
}
</ul>
</div>
)
}
export default Active;
import React from 'react'
import Todo from './Todo'
const Completed = (props) => {
const { completedItems } = props
return(
<div id="completedList">
<h2 className="position">Completed</h2>
<ul id="tasks">
{
completedItems.map(item => {
return(
<Todo key={item.id} title={item.title}/>
)
})
}
</ul>
</div>
)
}
export default Completed
import React from 'react';
class Todoform extends React.Component {
render(){
const {task, handleChange, handleSubmit} = this.props;
return(
<form onSubmit={handleSubmit}>
<label>Task description:</label>
<input type="text" name="name" placeholder="description" value={task} onChange={handleChange}/>
<button>Create New Task</button>
</form>
)
}
}
export default Todoform;
To hide the checkbox next to completed items you need to use Conditional Rendering. An example would be to add a prop IsCompleted to your component and use it when rendering html like this:
{this.props.isCompleted &&
<input
type="checkbox"
checked={this.state.checked}
onChange={this.handleCheck}
onClick={this.handleClick}/>
}
The duplicate item issue is probably because you use this.state.items in your handleComplete method instead of using this.state.completedItems if this is not the issue, would you mind sharing the code for the Todoform component as well?
EDIT: The item duplicates because when the handleComplete is called it copies this.state.items to the list and adds the one that you clicked on.
You should use this.state.completedItems in the handleComplete, also you are currently only sending and appending the title in the handleComplete method, you should be appending an object that has a title. The solution would be to update your handleClick method to this and update handleComplete to use this.state.completedItems:
handleClick = () => {
this.props.handlecompletedList({
title: this.props.title
});
};

How to implement this helper method without mutating state or refactoring to class-based component?

I am adding a feature to the survey form review for a user to be able to upload files and my concern is that I do not want to mutate state with this implementation, how do I refactor the below to ensure this? Is my only option refactoring it to a class-based component?
// SurveyFormReview shows users their form inputs for review
import _ from "lodash";
import React from "react";
import { connect } from "react-redux";
import { withRouter } from "react-router-dom";
import formFields from "./formFields";
import * as actions from "../../actions";
export const onFileChange = event => {
this.setState({ file: event.target.files });
};
const SurveyFormReview = ({ onCancel, formValues, submitSurvey, history }) => {
this.state = { file: null };
const reviewFields = _.map(formFields, ({ name, label }) => {
return (
<div key={name}>
<label>{label}</label>
<label>{formValues[name]}</label>
</div>
);
});
return (
<div>
<h5>Please confirm your entries</h5>
{reviewFields}
<h5>Add an Image</h5>
<input
onChange={this.onFileChange.bind(this)}
type="file"
accept="image/*"
/>
Or do I have no choice except to refactor this to a class-based component as a best course?
You should refactor this into a class. Something on the lines of this should work
class SurveyFormReview extends React.Component {
state = { file: null };
onFileChange = event => {
this.setState({ file: event.target.files });
};
render() {
const { onCancel, formValues, submitSurvey, history } = this.props
const reviewFields = _.map(formFields, ({ name, label }) => {
return (
<div key={name}>
<label>{label}</label>
<label>{formValues[name]}</label>
</div>
);
});
return (
<div>
<h5>Please confirm your entries</h5>
{reviewFields}
<h5>Add an Image</h5>
<input
onChange={this.onFileChange}
type="file"
accept="image/*"
/>
</div>
)
}
}
just as a note about optimizations and stuff.
Because this is a form, I'd recommend you use better html elements.
const reviewFields = _.map(formFields, ({ name, label }) => {
return (
<fieldset key={name}>
<span>{label}</span>
<span>{formValues[name]}</span>
</fieldset>
);
});
label elements are usually used with input elements.
fieldset elements are usually for form groups of data
you could use a legend element for the title of your fieldset if you wanted :)
If you don't want to touch existing code, you can create HOC
const withFile = (Component) => class extends React.Component {
state = { file: null }
render() {
return <Component {...this.props} file={file} onAttach={files => this.setState({ file: files }) />
}
}
export default withFile(SurveyForm)
Now your form will receive file and onAttach as props.

Binding and saving react button value

I am trying to save the value of the button as a string.If i click residence button it will save the value in categoryName as 'residence' or 'commercial' and redirect to another page .I have built a Rest API in the backend to bind and save the value in database.The code is something like this
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
class CustomizedButtons extends React.Component {
constructor(props) {
super(props);
this.state = {
apiUrl:config.publicRuntimeConfig.publicRuntimeConfigValue.apiUrl,
category: " ",
};
}
saveValue = () => {
console.log('savecategory');
axios.post( this.state.apiUrl+'/api/v1/leadsurvey/category', {
'categoryName':this.state.category,
}, {})
};
render() {
const { classes} = this.props;
return (
<div>
<div>
<p>What is the type of your property?</p>
<div>
<button onClick={() => this.saveValue()}>Residence</button>
<button onClick={() => this.saveValue()}>Commercial</button>
</div>
<div style={{marginTop: '90px'}}>
</div>
</div>
</div>
);
}
}
export default CustomizedButtons;
I am not getting how to make it work to bind and save.In case of saving form value i did something like this.
this.state = {
apiUrl:config.publicRuntimeConfig.publicRuntimeConfigValue.apiUrl,
FreeQuoteName :"",
};
this.handleFreeQuoteName = this.handleFreeQuoteName.bind(this);
saveFreeQuote = () => {
console.log('saveFreeQuote ...', this.state);
axios.post( this.state.apiUrl+'/api/v1/SalesLead/save', {
'name': this.state.FreeQuoteName,
}
}
handleFreeQuoteName(event) { this.setState({ FreeQuoteName: event.target.value }); }
<Form>
<p>Name*</p>
<input maxLength="30" onChange={this.handleFreeQuoteName} value={this.state.FreeQuoteName}
type="text" placeholder="Enter name here"/>
<div style={{textAlign:'center', marginTop:'35px', marginBottom:'22px'}} className={card.disable}>
<button disabled={isDisabled} type="button" fullwidth="true" variant="contained"
onClick={() => this.saveFreeQuote()} style={{padding: '9px 0px'}}>Submit</button>
</Form>
I want to do same for the value button.If i click the button it will save the value as a string and redirect to another page.How can i do it?
from your post I assumed that you want to save button value in state and also want to initiate the axios request while button click.
try to change like below
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import axios from 'axios';
class CustomizedButtons extends React.Component {
constructor(props) {
super(props);
this.state = {
apiUrl:config.publicRuntimeConfig.publicRuntimeConfigValue.apiUrl,
category: "",
};
}
saveValue = (e) => {
console.log('savecategory', e.target.innerHTML);
this.setState({
category: e.target.innerHTML
}, this.makeAxiosRequest);
};
makeAxiosRequest = () => {
axios.post( this.state.apiUrl+'/api/v1/leadsurvey/category', {
'categoryName':this.state.category,
}, {})
};
render() {
const { classes} = this.props;
return (
<div>
<div>
<p>What is the type of your property?</p>
<div>
<button onClick={this.saveValue}>Residence</button>
<button onClick={this.saveValue}>Commercial</button>
</div>
<div style={{marginTop: '90px'}}>
</div>
</div>
</div>
);
}
}
export default CustomizedButtons;
here am using callback function inside setState() to initiate axios request after button value saved in state.
Hope this helps.

save range Slider value in state and initiate the axios request while button click

I am trying to save the Range-slider value .If i click "NEXT" i want it save the value in 'Squarefeet' variable and redirect to another page .I have built a Rest API in the backend to bind and save the value in database.The code is something like this
import React, { Component } from 'react'
import Slider from 'react-rangeslider'
import Link from "next/link";
import axios from "axios";
import getConfig from "next/config";
const config = getConfig();
class Horizontal extends Component {
constructor (props, context) {
super(props, context)
this.state = {
apiUrl:config.publicRuntimeConfig.publicRuntimeConfigValue.apiUrl,
}
}
handleChangeStart = () => {
console.log('Change event started')
};
handleChange = value => {
this.setState({
value: value
})};
handleChangeComplete = () => {
console.log('Change event completed')
};
saveValue = () => {
console.log('saveValue ...', this.state);
axios.post( this.state.apiUrl+'/api/v1/LeadSurvey/save', {
'squareFeet':this.state.value,
}, {} )
};
render () {
const { value } = this.state
return (
<div>
<div className='slider' style={{ marginTop:'165px',marginLeft:'319px',width:'700px',backgroundColor:'EF5350'}} >
<Slider min={850} max={5000} value={value} onChangeStart={this.handleChangeStart}
onChange={this.handleChange}
onChangeComplete={this.handleChangeComplete}
/>
<div className='value'>{value} Squarefeet</div>
<div style={{marginTop:'86px'}}>
<Link prefetch href="/estimate"><a href="#" >
<span onChange={this.handleChange} onClick={() => this.saveValue()} >Next</span></a></Link>
</div>
</div>
</div>
)
}
}
export default Horizontal
I am not getting how to make it work to bind and save.How should i do it?

Resources