Handle Redux-form events - javascript

Not really sure how to do this, quite new in the Redux-Form world. I have a custom component for my inputs with an onFocus event that displays an unordered list and an onBlur event that hides it. I am also attaching an onClick event to the items because I would like to change the value of the input field once I click on a list item but the click event never fires because blur fires first.
Not sure if I am addressing my code properly.
Here is my custom component:
import React, { Component } from 'react'
import pageStyles from '../../../styles.scss'
class MyInput extends Component {
constructor(props) {
super(props);
this.state = {
dropdownIsVisible: false,
dropdownCssClass: 'dropdown'
}
this.handleFocus = this.handleFocus.bind(this);
this.handleBlur = this.handleBlur.bind(this);
this.handleClick = this.handleClick.bind(this);
}
handleFocus () {
this.setState({
dropdownIsVisible: true,
dropdownCssClass: ['dropdown', pageStyles.dropdown].join(' ')
})
}
handleBlur () {
this.setState({
dropdownIsVisible: false,
dropdownCssClass: 'dropdown'
})
}
handleClick () {
console.log('here I am')
}
render() {
const {
input: {
name,
value,
onFocus
},
label,
hasOptions,
options,
cssClass,
meta: {
touched,
error
}
} = this.props
if(options) {
var listItems = options.map((item) =>
<li key={ item.id } onClick={ this.handleClick }>{ item.name }</li>
)
}
return (
<div className={ cssClass }>
<label>{ label }</label>
<input name={ name } id={ name } type="text" onFocus={ this.handleFocus } onBlur={ this.handleBlur } autoComplete="off" />
{ hasOptions === true ? <ul className={ this.state.dropdownCssClass }>{ listItems }</ul> : null }
{ touched && error && <span className="error">{ error }</span> }
</div>
)
}
}
export default MyInput

Use lodash debounce function. This function adds delay before function calling, so you can cancel it in case of nested element click.
Add in constructor:
this.handleBlur = debounce(this.handleBlur, 100);
replace handleClick:
handleClick () {
if(this.handleBlur.cancel){
this.handleBlur.cancel()
}
console.log('here I am')
}

Related

How to close the drop-down after selecting a value in react?

I have used a drop-down in my React project. When I select a value I want to close it. But it doesn't automatically close. How can I do it ?
Dropdown.js
import React from 'react';
import { PropTypes } from 'prop-types';
import { DropdownToggle, DropdownMenu, UncontrolledDropdown } from 'reactstrap';
import * as Icon from 'react-bootstrap-icons';
import './DropDown.scss';
/**
* This is a reusable dropdown
* onClick function and dropdown items come as props
*/
class DropDown extends React.Component {
render() {
const { dropDownItemArray, text, onClick } = this.props;
const dropdownItems = dropDownItemArray.map((item,key) => {
return (
<div className="dropdown-items" onClick={onClick} >
{item}
</div>
);
});
return (
<div>
<UncontrolledDropdown className="multi-select-wrapper text p4">
<DropdownToggle className="select-dropdown">
<div className="select-text text p4">{text}</div>
<Icon.CaretDownFill />
</DropdownToggle>
<DropdownMenu name='test'>{dropdownItems}</DropdownMenu>
</UncontrolledDropdown>
</div>
);
}
}
DropDown.propTypes = {
text: PropTypes.string,
onClick: PropTypes.func,
menuItemArray: PropTypes.array
};
export default DropDown;
This handles all the input values from the input fields and selected values from dropdowns.
handleChangeInputs = (event) => {
if (event[0] === '<') {
this.setState({
editorHtml: event
});
} else {
if (event.type === 'change') {
this.setState({
[event.target.name]: event.target.value
});
} else {
if (event.target.parentNode.innerText[0] === 'C') {
console.log(event);
this.setState({
ticketType: event.currentTarget.textContent
});
} else {
console.log("test");
this.setState({
ticketPriority: event.currentTarget.textContent
});
}
}
}
};
This part is related to drop-down handling
if (event.target.parentNode.innerText[0] === 'C') {
console.log(event);
this.setState({
ticketType: event.currentTarget.textContent
});
} else {
console.log("test");
this.setState({
ticketPriority: event.currentTarget.textContent
});
}
You could add a toggleDropdown function along with a property in your state dropdownOpen which will cause the dropdown to be open or closed:
toggleDropdown = () => {
const dropdownOpen = this.state.dropdownOpen ? false : true;
this.setState({ dropdownOpen: dropdownOpen})
};
Pass toggleDropdown and dropdownOpen in the props and reference them in your code:
const { toggleDropdown, dropdownOpen } = this.props;
[...]
<UncontrolledDropdown
isOpen={dropdownOpen || false}
toggle={toggleDropdown}
>
Your code references an onClick function but you've named your function handleChangeInputs. Here's an onClick function that would work.
onClick = (event) => {
this.setState({ ticketType: event.currentTarget.textContent }, () => this.toggleDropdown()}
Calling toggleDropdown inside of onClick only seems to work if it's in the callback of this.setState.

Is it possible to add Edit functionality to this Todo App made using ReactJS?

I have created a Todo List app successfully to some extent.
My constructor function of TodoList is as follows:
class TodoList extends React.Component {
constructor(props) {
super(props);
this.state = { value: '',
todoList: [{ id: 1, content: "Call Client" },
{ id: 2, content: "Write Log" }] }
this.onChangeValue = this.onChangeValue.bind(this);
this.onAddItem = this.onAddItem.bind(this);
}
The remaining body of TodoList has Add Item functionality, by using two methods onChangeValue and onAddItem
onChangeValue = event => {
this.setState({ value: event.target.value });
};
onAddItem = () => {
if (this.state.value !== '') {
this.setState(state => {
const todoList = state.todoList.concat({ id: state.todoList.length + 1, content: this.state.value});
return {
todoList,
value: '',
};
});
}
};
render() {
const listItems = this.state.todoList.map((todo) =>
<ListItem key={todo.id} id={todo.id} content={todo.content}/>
)
return <>
<ul className="todo-list">
{listItems}
</ul>
{/* <AddItem/> */}
<div className="add-item">
<input type="text" onChange={this.onChangeValue}/>
<button type="submit" onClick={this.onAddItem}>Add Item</button>
</div>
</>
}
}
Delete functionality and Mark as read functionality are created in the ListItem component using methods handleChange and handleDeleteClick.
class ListItem extends React.Component {
constructor(props) {
super(props);
this.state = { done : false, editing : '', deleted : false }
this.handleChange = this.handleChange.bind(this);
this.handleDeleteClick = this.handleDeleteClick.bind(this);
}
handleChange(event) {
this.setState({done: event.target.checked})
}
handleDeleteClick() {
this.setState({ deleted : true })
}
render() {
if (this.state.deleted === false) {
return <li className="list-item">
{/* special class is added to the paragraph to strike the text when marked as done */}
<p className={this.state.done ? 'done' : ''}>{this.props.content}</p>
<ul className="actions">
<li>
<label htmlFor={'item_' + this.props.id}>Mark as done</label>
<input name={'item_' + this.props.id} id={'item_' + this.props.id} type="checkbox" onChange={this.handleChange}/>
</li>
<li>
{/* Edit button is disabled once the task is marked as done */}
{ this.state.done ? <button type="button" disabled>Edit</button> : <button type="button">Edit</button> }
</li>
<li><button type="button" onClick={this.handleDeleteClick}>Delete</button></li>
</ul>
</li>
}
else {
return null;
}
}
}
Now the only thing remaining is the edit functionality which I cannot figure out if it's possible or not.
The source code of my application can be found in this codepen:
https://codepen.io/blenderous/pen/rNeywyZ
We could create an onEditItem method inside the TodoList item and pass this method to each ListItem. This method would receive an id and a newContent values to process the updates.
// TodoList component
...
onEditItem = (id, newContent) => {
const newTodoList = this.state.todoList.map((todo) => {
// return todo.id !== id ? todo : { ...todo, content: newContent }
// not same id? leave as is
if (todo.id !== id) {
return todo;
}
// update content with the newContent value
return { ...todo, content: newContent };
});
this.setState({ todoList: newTodoList });
};
Then on our ListItem, we'll create an handleEditClick method that will handle the click event for our edit button.
// ListItem component
...
handleEditClick() {
const { id, content } = this.props;
// prompt to edit the current content
const newContent = prompt("Edit:", content);
// call the TodoList editTodo passing the id and the new content
// of the current todo
this.props.editTodo(id, newContent);
}
Now we'll use this method on our edit button like so
...
<button
type="button"
disabled={this.state.done} // disabled once the task is marked as done
onClick={this.handleEditClick}
>
Edit
</button>

Input doesn't work when used with debounce, event.persist() and storing value at parent component

I need an input field with debounced search and value should be passed from parent component. But it doesn't work when value passed from parent component. What is the right way to implement it?
Codesandbox example https://codesandbox.io/embed/debounce-input-owdwj
Text field with debouncing
class MyTextField extends Component {
search = _.debounce(event => {
this.props.onChange(event.target.value);
}, 300);
handleChangeInput = event => {
event.persist();
this.search(event);
};
render() {
return (
<TextField
value={this.props.value}
placeholder="type"
onChange={this.handleChangeInput}
/>
);
}
}
Parent component storing the value of text field
class Form extends Component {
state = {
value: ""
};
handleChangeInput = value => {
this.setState({
value
});
};
render() {
return (
<div>
<h2>{this.state.value}</h2>
<MyTextField
value={this.state.value}
onChange={this.handleChangeInput}
/>
</div>
);
}
}
The problem here is that you are updating the component only after 300 seconds which will not update the input box also. first, you need to update the search box component whenever there is a keyup and later parent can be informed about the changed after 300 seconds
I have updated your code reference please check it out https://codesandbox.io/embed/debounce-input-gm50t
Declare your debounce function in componentDidMount is everything will be fine.
1) Without state
class MyTextField extends Component {
handleChangeInput = e => {
this.search(e.target.value)
};
componentDidMount() {
this.search =_.debounce((value) => {
this.props.onChange(value);
}, 300)
}
render() {
return (
<TextField
value={this.props.value}
placeholder="type"
onChange={this.handleChangeInput}
/>
);
}
}
export default MyTextField;
2) With state:
class MyTextField extends Component {
state = {
textValue: ''
}
handleChangeInput = e => {
this.setState({
textValue: e.target.value
}, () => { this.search()})
};
componentDidMount() {
this.search =_.debounce(() => {
this.props.onChange(this.state.textValue);
}, 300)
}
render() {
return (
<TextField
value={this.props.value}
placeholder="type"
onChange={this.handleChangeInput}
/>
);
}
}
export default MyTextField;
Hope that helps!!!

React form sending empty object

I am trying to send user input in a form component to the component managing the state. I tried to use a callback but my input is not being sent.
Tried to use the form object in a callback
//here is the form component
class ListForm extends React.Component {
constructor(props) {
super(props);
this.state = {
NewItem: {
itemText: "",
id: Date.now(),
completed: false
}
};
}
handleChanges = e => {
this.setState({...this.state,
NewItem: { ...this.state.NewItem, [e.target.name]: e.target.value }
});
};
submitItem = e => {
this.setState({ itemText: "" });
this.props.addItem(e, this.state.NewItem);
};
render() {
return (
<form onSubmit={this.submitItem}>
<input
type="text"
value={this.state.NewItem.itemText}
name="itemText"
onChange={this.handleChanges}
/>
<button>Add</button>
</form>
);
}
}
export default ListForm;
addItem is in the parent component and looks like this. Groceries is just an array of objects with itemText, id and completed
addItem = (e, item) => {
e.preventDefault();
this.setState({
...this.state,
groceries: [...this.state.groceries, item]
});
};
I can enter in the form, but when I hit enter, nothing is added to the list
Very first thing to try is to add e.persist to your addItem() function
addItem = (e, item) => {
e.preventDefault();
e.persist()
this.setState({
...this.state,
groceries: [...this.state.groceries, item]
});
};
Another option is using redux to save the form data in the Global state and then accessing it in the parent component through redux. Here is an example of what that might look like:
import React, { Component } from 'react';
import * as ACTIONS from '../store/actions/actions';
import { connect } from 'react-redux';
class Form1 extends Component {
state ={
value: ''
}
handleChange = (event) => (
this.setState({value: event.target.value})
)
handleSubmit = (event) => {
event.preventDefault()
this.props.input_action_creator(event.target.name.value)
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<input id="name" onChange={this.handleChange} type="text" />
<button type="submit"> Submit </button>
</form>
<br />
</div>
)}
}
function mapDispatchToProps(dispatch) {
return {
input_action_creator: (text) => dispatch(ACTIONS.user_input(text))
}
}
export default connect(mapDispatchToProps)(Form1);
class Parent extends Component {
access form data with
this.props.user_Text
}
function mapStateToProps(state) {
return {
user_text: state.user_reducer.user_text
}
}
here is a fully funcitoning react-redux project if you are interested
https://github.com/iqbal125/modern-react-app-sample

how do i set programmatically checkbox property 'checked' or 'defaultChecked' in react

i am developing web application with react.
I want to dynamically set 'checked' property on checkbox.
It showed a proper screen that i wanted, but not working when i programmatically set 'checked' property.
So, i have searched about this.
some people recommended to use 'defaultChecked' property.
ant then i tried this. but failed.
It has not changed when i had given event that programmatically set checked property. It always showed defaultChecked value.
source code
render() {
list = {
resource: {[...], [...] },
...
}
return (
<ParameterContainer values={ list } />
);
}
class ParameterContainer extends React.Component {
constructor(props) {
super(props);
}
render() {
const { values } = this.props;
return (
<div className="parameter-table">
<div className="parameter-row-container">
<ul className="parameter-row title">
<li className="parameter parameter-1">valid</li>
<li className="parameter parameter-2">Key</li>
<li className="parameter parameter-3">Type</li>
<li className="parameter parameter-4">oid</li>
<li className="parameter parameter-5">rid</li>
<li className="parameter parameter-6">desc</li>
</ul>
{
values.resource.map((value, i) => {//one row
return (
<ParameterItem value={ value } index={ i } key={ i }/>
);
})
}
</div>
</div>
);
}
}
class ParameterItem extends React.Component {
constructor(props) {
super(props);
}
render() {
const { value, index } = this.props;
const items = [];
for (let o in value) {
if (o === "valid") {
continue;
}
items.push(value[o]);
}
return (
<ul className={`parameter-row parameter-row-${ index+1 }`}>
<li className="parameter parameter-1">
<input type="checkbox" defaultValue={ value.valid } checked={ value.valid } onChange={ this.toggle }/> //i tried to this.
<Checkbox isChecked={ value.valid } /> // and then tried this...
</li>
</ul>
);
}
toggle(e) {
$(e.target).prop("checked", e.target.checked ? 0 : 1);
}
}
class Checkbox extends React.Component {
constructor(props) {
super(props);
this.state = {
checked: !!this.props.isChecked
};
}
handleChange = (e) => {
const { target: { checked }} = e;
this.setState({ checked });
}
render() {
return (
<input type="checkbox" checked={ this.state.checked } onChange={ this.handleChange } />
);
}
}
Your onChange method in checkbox input is missing a binder.
Try this: onChange={this.handleChange.bind(this)}.
More explanation of the usage of .bind(this) can be found here: Handling events
You're looking for controlled components, where the value of the component is dictated by state and updated thru an onchange handler. Check out https://reactjs.org/docs/forms.html#controlled-components
I had a similar problem. I wanted to list a set of checkboxes, but some of them might be already checked.
I solved it like this:
{this.props.checkboxesList.map(e => (
<CustomInput
type="checkbox"
key={e.id}
id={e.id}
name={e.nombre}
label={e.nombre}
defaultChecked={
this.props.form.selected.filter(
item => item.id === e.id
).length === 0
? false
: true
}
onChange={this.props.onCheckBoxChange}
/>
))}
Note: CustomInput is from 'reactstrap'
And my "onCheckBoxChange" looks like this:
handleCheckBoxChange = e => {
let options = this.state.form.selectedOptions;
if (e.target.checked) {
options.push({ id: e.target.id, nombre: e.target.name });
} else {
options = options.filter(item => item.id !== e.target.id);
}
this.setState({
form: {
...this.state.form,
selectedOptions: options
}
});};
Have into account that "state.from.selectedOptions" must be an array.
Hope it helps

Categories

Resources