React Bootstrap CheckBox not toggling 'checked' Prop on Selection - html

I am fairly new to React Bootstrap and I have been having an annoying issue with checkboxes in a form I am creating.
They won't stay checked when selected after my state updates. They will if I check them again.
I created a toggling function that appends input from selected check boxes in state and sets the checked prop to true. checked={true}
I have written it two ways, both not working.
handleToggle(e) {
e.preventDefault()
const selectedBox = "cb" + e.target.value
this.setState({ goals: this.state.goals + e.target.value, [selectedBox]: e.target.checked })
}
handleToggle(e) {
e.preventDefault()
const selectedBox = "cb" + e.target.value
this.setState({ goals: this.state.goals + e.target.value, [selectedBox]: true })
}
What has been frusturating is that proper values are updating in the state. I threw a debugger in and can see the current state containing a true value for the selected check boxes and the user input appending to whatever is currently under the goals key.
Any direction appreciated. This has been taking a while to debug. Thanks.
Full component -
import React from 'react';
import { connect } from 'react-redux';
import { Button, form, FormGroup, Checkbox, Radio, option, ControlLabel, FormControl, ProgressBar, Pagination, Form } from 'react-bootstrap';
import DatePicker from "react-bootstrap-date-picker";
import { handleChange } from '../helpers';
class Portfolio extends React.Component {
constructor(props) {
super(props)
var value = new Date().toISOString();
this.state = {
date: value,
experience: 1,
progress: 0,
active: false,
goals: "",
cb1: false,
cb2: false,
cb3: false,
cb4: false,
cb5: false
}
this.handleSelect = this.handleSelect.bind(this)
this.handleToggle = this.handleToggle.bind(this)
}
handleSelect(eventKey) {
if (this.state.active === false) {
this.setState({ experience: eventKey, progress: this.state.progress += 20, active: true })
} else {
this.setState({ experience: eventKey })
}
}
handleToggle(e) {
e.preventDefault()
const selectedBox = "cb" + e.target.value
this.setState({ goals: this.state.goals + e.target.value, [selectedBox]: e.target.checked })
}
render() {
const stats = this.props.user.stats
if (!stats || stats.length === 0) {
return(
<div className="portfolio-form-main">
<div className="portfolio-form-container-title-div">
<h1 className="portfolio-title">Profile Information</h1>
</div>
<div className="portfolio-form-container">
<form className="portfolio-form">
<ProgressBar active now={this.state.progress} />
<FormGroup>
<ControlLabel>Choose Your Goals.</ControlLabel>
<Checkbox checked={this.state.cb1} onChange={this.handleToggle} value="1" >
Lose Some Weight
</Checkbox>
{' '}
<Checkbox checked={this.state.cb2} onChange={this.handleToggle} value="2">
Build Strength and Muscle
</Checkbox>
{' '}
<Checkbox checked={this.state.cb3} onChange={this.handleToggle} value="3">
General Health and Wellness
</Checkbox>
{' '}
<Checkbox checked={this.state.cb4} onChange={this.handleToggle} value="4">
Compete in an Event
</Checkbox>
{' '}
<Checkbox checked={this.state.cb5} onChange={this.handleToggle} value="5">
Rehab an Injury
</Checkbox>
</FormGroup>
<FormGroup>
<ControlLabel>Rate Your Exercise Experience Level.</ControlLabel>
<Pagination
bsSize="medium"
items={10}
activePage={this.state.experience}
onSelect={this.handleSelect}
/>
</FormGroup>
<FormGroup>
<ControlLabel>When is Your Birthday?</ControlLabel>
{' '}
<DatePicker value={this.state.value}/>
</FormGroup>
<ControlLabel>How Tall Are You?</ControlLabel>
{' '}
<Form inline>
<FormGroup>
<FormControl type="number"/>
{' '}
<FormControl componentClass="select" placeholder="select">
<option value="select">Unit</option>
<option value="other">in</option>
<option value="other">cm</option>
</FormControl>
</FormGroup>
</Form>
<ControlLabel>How Much Do You Weigh?</ControlLabel>
{' '}
<Form inline>
<FormGroup>
<FormControl type="number"/>
{' '}
<FormControl componentClass="select" placeholder="select">
<option value="select">Unit</option>
<option value="other">Lbs</option>
<option value="other">Kgs</option>
</FormControl>
</FormGroup>
</Form>
<FormGroup >
<ControlLabel>Tell Us About Yourself.</ControlLabel>
{' '}
<FormControl componentClass="textarea" placeholder="textarea" />
</FormGroup>
<Button bsStyle="primary">
Submit
</Button>
</form>
</div>
</div>
)
}
return(
<div>
<ul>
<li>{stats.birthdate}</li>
<li>{stats.weight} {stats.weight_unit}</li>
<li>{stats.height} {stats.height_unit}</li>
<li>{stats.experience}</li>
<li>{stats.about_me}</li>
</ul>
</div>
)
}
}
export default Portfolio

Removing e.preventDefault() from the handleToggle function in your answer should eliminate the need for a forced page refresh.
When a checkbox is clicked React compares the previous value with the next value to decide whether a change has occurred. If it has then a change event is queued.
It seems that React queries the DOM for the checked value before handleToggle is called, so the checkbox value is True. Then preventDefault() executes so the checkbox value in the DOM is now False, but React has set the value as True.
So when using preventDefault() with checkboxes (and possibly radio buttons) you get inconsistencies between the DOM and React.

This works perfectly for selecting and deselecting checkboxes and setting state, adding and removing checked values.
handleToggle(e) {
e.preventDefault()
const selectedBox = "cb" + e.target.value
if (this.state.goals.includes(e.target.value)) {
const goal = this.state.goals.replace(e.target.value, '')
this.setState({ goals: goal, [selectedBox]: e.target.checked })
} else {
this.setState({ goals: this.state.goals + e.target.value, [selectedBox]: e.target.checked })
}
this.props.requestUser(this.props.match.params.userId);
}

Related

Getting type of HTML element's attributes

I would like to create a React component in Typescript that has all the same attributes as the HTML <select> element.
How would I type this?
type Props = {
label: string
} & HTMLSelectElementAttributes; // This one is wrong
function MySelect(props: Props) {
return <label>{props.label}</label><select {...props}>
<option value="A">A</option>
<option value="B">B</option>
</select>;
}
In a type-aware IDE like VSCode, if you hover over the <select, you'll see:
React.DetailedHTMLProps<React.SelectHTMLAttributes<HTMLSelectElement>, HTMLSelectElement>
You can use that as the type for the other props.
type Props = {
label: string;
} & React.DetailedHTMLProps<React.SelectHTMLAttributes<HTMLSelectElement>, HTMLSelectElement>; // This one is wrong
const MySelect = (props: Props) => (
<div>
{' '}
<label>
label
{props.label}
</label>
<select {...props} />
</div>
);
// Example usage in which no type warnings result
const X = () => <MySelect label="foo" onChange={() => { console.log('change'); }} />;

React forum submit when pressing enter in textarea

A am making this forum type app and to create a post you go through a form with a textarea; I want to support Markdown. So to do that you kind of need to support new line functionality. When I press the enter key this error occurs:
This is the code
export default class CreatePost extends Component {
state = {
redirect: false,
Title: '',
Author: '',
Body: '',
};
updateState = e => {
this.setState({ [e.target.name]: e.target.value });
};
createPost = e => {
e.preventDefault();
if (this.state.Body !== '') {
this.props.createPost(this.state);
}
this.setState({ Title: '', Author: '', Body: '', redirect: true });
};
onEnterPress = e => {
if (e.keyCode === 13) {
e.preventDefault();
}
};
render() {
if (this.state.redirect) {
return <Redirect push to='/' />;
}
return (
<div className='createPost'>
<h1>Create Your Post!</h1>
<form onSubmit={this.createPost}>
<label className='input'>
Title:{' '}
<input
type='text'
name='Title'
value={this.state.Title}
onChange={this.updateState}
required
/>
</label>
<label className='input'>
Author:{' '}
<input
type='text'
name='Author'
value={this.state.Author}
onChange={this.updateState}
required
/>
</label>
<textarea
name='Body'
className='body'
value={this.state.Body}
onKeyDown={this.onEnterPress}
onChange={this.updateState}
></textarea>
<input type='submit' value='Submit' className='submitBTN' />
</form>
<Link className='home' to='/'>
Go Home
</Link>
</div>
);
}
}
I have tried the regular on press down function but It doesn't work.
Update #1
I got it to work but I still get the error.
You need to move the onEnterPress to the form itself, because the form also listens to the key presses.
<form onSubmit={this.createPost} onKeyDown={onEnterPress}>
Here is a fiddle.

JavaScript React Redux: Field from Database is not Rendering in Web Page and Can't Select Value in DropDown

I have a table with a field called isAdmin on the back-end. The isAdmin field is either true or false for different users.
Below is my react/redux code that renders that front-end using the field names where applicable:
import React from 'react'
import {connect} from 'react-redux'
import {getUsers} from '../store/allUsers' // toDate
import {updateUserThunk, fetchSingleUser} from '../store/singleUser'
// Status Filter import BeerFilter from './BeerFilter'
import Card from 'react-bootstrap/Card'
import Button from 'react-bootstrap/Button'
import {UncontrolledCollapse} from 'reactstrap'
export class AllUsers extends React.Component {
constructor(props) {
super(props)
this.state = {
showForm: false,
isAdmin: ''
}
this.handleChange = this.handleChange.bind(this)
this.handleSubmit = this.handleSubmit.bind(this)
}
componentDidMount() {
try {
this.props.fetchInitialUsers()
} catch (error) {
console.error(error)
}
}
clickHandlerOne() {
let hidden = this.state.showForm
this.setState({
showForm: !hidden
})
}
handleChange(event) {
//console.log('event.target', event.target)
this.setState({
[event.target.name]: event.target.value
})
}
async handleSubmit(userId) {
event.preventDefault()
const updatedUser = {
id: userId,
isAdmin: this.state.isAdmin
}
// console.log('UPDATE USER', updatedUser)
await this.props.updateUserThunk(updatedUser)
this.props.fetchInitialUsers()
}
render() {
const users = this.props.users
// console.log('PROPS', this.props)
console.log('USERS', this.props.users)
return (
<div>
{/* <div className="options">
<select onChange={this.handleChange}>
<option value="">Sort By...</option>
<option value="priceHighToLow">Price (high to low)</option>
<option value="priceLowToHigh">Price (low to high)</option>
<option value="name">Name</option>
</select>
<BeerFilter />
</div> */}
<div className="flex-cards">
{users.map(user => (
<Card style={{width: '18rem'}} key={user.id}>
<Card.Body>
<Card.Title>User Id: {user.id}</Card.Title>
<Card.Text>
<div>
<ul>
<li>
<div className="highlight">
<img src={user.imageUrl} />
</div>
<div className="details">
<p>Username: {user.username}</p>
<p>User Email: {user.email}</p>
<p>Admin Status: {user.isAdmin}</p>
<p>
Created Date:{' '}
{new Intl.DateTimeFormat('en-GB', {
month: 'short',
day: '2-digit',
year: 'numeric'
}).format(new Date(user.createdAt))}
</p>
<p />
<Button
id={`user${user.id}`}
onClick={() => {
this.clickHandlerOne()
}}
variant="danger"
>
Admin Status Toggle
</Button>
<UncontrolledCollapse toggler={`#user${user.id}`}>
{/* {this.state.showForm && (
<UpdateUserStatus userId={user.id} />
)} */}
<form onSubmit={() => this.handleSubmit(user.id)}>
<div>
<span>
<select
name="isAdmin"
value={
typeof user.isAdmin === 'string'
? this.state.isAdmin
: user.isAdmin
}
onChange={this.handleChange}
>
<option value="true">true</option>
<option value="false">false</option>
</select>
</span>
<p />
<span>
<p>
{/* */}
<button type="submit">Submit</button>
</p>
</span>
{/* delete thunk
<span>
<p>
<button type="button">Delete</button>
</p>
</span>
*/}
</div>
</form>
</UncontrolledCollapse>
</div>
</li>
</ul>
</div>
</Card.Text>
</Card.Body>
</Card>
))}
</div>
</div>
)
}
}
const mapStateToProps = state => {
return {
users: state.allUsers
}
}
const mapDispatchToProps = dispatch => {
return {
loadSingleUser: id => dispatch(fetchSingleUser(id)),
updateUserThunk: updatedUser => dispatch(updateUserThunk(updatedUser)),
//getSortedBeers: (sortBy, beers) => dispatch(sortBeers(sortBy, beers)),
fetchInitialUsers: () => dispatch(getUsers())
}
}
export default connect(mapStateToProps, mapDispatchToProps)(AllUsers)
I have two issues with trying to render the isAdmin field. First, the true or false value associated with the isAdmin field never gets rendered.
See pic:
Question 1: how can I get the value of isAdmin field to render?
My second question is related to updating my isAdmin field and rendering the update on the web page.
After I click on the red toggle button, I have a drop down field where I can select true or false. It is defaulted on the value "true" or "false" from the database. But I can't seem to select the other drop down value. It remains stuck on the default selection.
For example, the drop down defaulted on false. When I open the drop down and select true, it doesn't select true ... it still has false selected.
Question 2: What am I doing wrong?
The end goal is to be able to select the opposite of what is defaulted in the dropdown and update my Admin Status to the selected status.
1.user.isAdmin is a boolean false, React does not render such value. You can fix this by rendering a string :
{user.isAdmin ? 'Yes' : 'No'}
2.When you render the select field, you use a condition in the value :
typeof user.isAdmin === 'string' ? this.state.isAdmin : user.isAdmin
because user.isAdmin is boolean, the value prop will always be user.isAdmin. You can fix this by using this.state.isAdmin directly :
value={this.state.isAdmin}

React: State update delay

I'm trying to change state by checking radio button. When I check it, it updates the value only after I check the next radio button. If I click first radio button it won't change the state, and if I check the second one it updates state with the previously checked radio button's value. Can anyone help me fixing this?
class App extends React.Component {
state = { checked: false, radioValue: '' }
handleChange = (event) => {
const target = event.target;
const value = target.value;
const name = target.name;
console.log("this.state", this.state); // Gets previous value
this.setState({
[name]: value
});
}
render() {
return (
<div className="wrapper">
<input
name="radioValue"
type="radio"
value="aaa"
checked={this.state.radioValue === 'aaa'}
onChange={this.handleChange} />First Radio Button
<br />
<input
name="radioValue"
type="radio"
value="bbb"
checked={this.state.radioValue === 'bbb'}
onChange={this.handleChange} />Second Radio Button
</div>
);
}
}
export default App;
this.setState({
[name]: value
},()=>console.log(this.state));
you can also check like this by using callback in setstate
Please try this.
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
checked: ""
};
}
handleChange = (event) => {
console.log("this.state", this.state); // Gets previous value
this.setState({
checked: event.target.value
});
}
render() {
return (
<div className="wrapper">
<input name="radioValue" type="radio" value="aaa"
checked={this.state.checked === 'aaa'}
onChange={this.handleChange} />
First Radio Button
<br />
<input name="radioValue" type="radio" value="bbb"
checked={this.state.checked === 'bbb'}
onChange={this.handleChange} />
Second Radio Button
</div>
);
}
}
It works well on my machine.

How to reset ReactJS file input

I have file upload input:
<input onChange={this.getFile} id="fileUpload" type="file" className="upload"/>
And I handle upload this way:
getFile(e) {
e.preventDefault();
let reader = new FileReader();
let file = e.target.files[0];
reader.onloadend = (theFile) => {
var data = {
blob: theFile.target.result, name: file.name,
visitorId: this.props.socketio.visitorId
};
console.log(this.props.socketio);
this.props.socketio.emit('file-upload', data);
};
reader.readAsDataURL(file);
}
If I upload same file twice, then upload event is not fired. How can I fix that? For simple js code it was enough to do the following: this.value = null; in change handler. How can I do it with ReactJS?
I think you can just clear the input value like this :
e.target.value = null;
File input cannot be controlled, there is no React specific way to do that.
Edit For old browsers (<IE11), you can use one of the following techniques.
See http://jsbin.com/zurudemuma/1/edit?js,output (tested on IE10 & 9)
What worked for me was setting a key attribute to the file input, then when I needed to reset it I update the key attribute value:
functionThatResetsTheFileInput() {
let randomString = Math.random().toString(36);
this.setState({
theInputKey: randomString
});
}
render() {
return(
<div>
<input type="file"
key={this.state.theInputKey || '' } />
<button onClick={this.functionThatResetsTheFileInput()} />
</div>
)
}
That forces React to render the input again from scratch.
This work for me - ref={ref => this.fileInput = ref}
<input id="file_input_file" type="file" onChange={(e) => this._handleFileChange(e)} ref={ref=> this.fileInput = ref} />
then in my case once the file was uploaded to the server , I clear it by using the statement below
this.fileInput.value = "";
I do it by updating key inside my file input.
This will force a re-render and previously selected file will go away.
<input type="file" key={this.state.inputKey} />
Changing the state inputKey will re-render the component.
One way to change the inputKey will be to always set it to Date.now() on click of a button which is supposed to clear the field.
With every click onClick you can reset the input, so that even with the same file onChange will be triggered.
<input onChange={this.onChange} onClick={e => (e.target.value = null)} type="file" />
import React, { useRef } from "react";
export default function App() {
const ref = useRef();
const reset = () => {
ref.current.value = "";
};
return (
<>
<input type="file" ref={ref} />
<button onClick={reset}>reset</button>
</>
);
}
The following worked for me using React Hooks. This is done using what is known as a "controlled input". That means, the inputs are controlled by state, or their source of truth is state.
TL;DR Resetting the file input was a two-step process using both the useState() and useRef() hooks.
NOTE: I also included how I reset a text input in case anyone else was curious.
function CreatePost({ user }) {
const [content, setContent] = React.useState("");
const [image, setImage] = React.useState(null); //See Supporting Documentation #1
const imageInputRef = React.useRef(); //See Supporting Documentation #2
function handleSubmit(event) {
event.preventDefault(); //Stop the pesky default reload function
setContent(""); //Resets the value of the first input - See #1
//////START of File Input Reset
imageInputRef.current.value = "";//Resets the file name of the file input - See #2
setImage(null); //Resets the value of the file input - See #1
//////END of File Input Reset
}
return (
<div>
<form onSubmit={handleSubmit}>
<input
type="text"
placeholder="Add Post Content"
onChange={event => setContent(event.target.value)}
value={content} //Make this input's value, controlled by state
/>
<input
type="file"
onChange={event => setImage(event.target.files[0])} //See Supporting Doc #3
ref={imageInputRef} //Apply the ref to the input, now it's controlled - See #2
/>
<button type="submit">Submit Form</button>
</form>
</div>
)
};
Supporting Documentation:
useState Hook
Returns a stateful value, and a function to update it.
useRef Hook
If you pass a ref object to React, React will set its current property to the corresponding DOM node whenever that node changes.
Using files from web apps
If the user selects just one file, it is then only necessary to consider the first file of the list.
You can also include this in your input element if you know you are not going to be using the built-in file input value at all.
<input value={""} ... />
This way the value is always reset to the empty string on render and you don't have to include it awkwardly in an onChange function.
I know file input is always uncontrolled however the following code still works in my own porject, I can reset the input with no problems at all.
constructor(props) {
super(props);
this.state = {
selectedFile: undefined,
selectedFileName: undefined,
imageSrc: undefined,
value: ''
};
this.handleChange = this.handleChange.bind(this);
this.removeImage = this.removeImage.bind(this);
}
handleChange(event) {
if (event.target.files[0]) {
this.setState({
selectedFile: event.target.files[0],
selectedFileName: event.target.files[0].name,
imageSrc: window.URL.createObjectURL(event.target.files[0]),
value: event.target.value,
});
}
}
// Call this function to reset input
removeImage() {
this.setState({
selectedFile: undefined,
selectedFileName: undefined,
imageSrc: undefined,
value: ''
})
}
render() {
return (
<input type="file" value={this.state.value} onChange={this.handleChange} />
);
}
We can reset file input by using key = {this.state.fileInputKey} and initialsing fileInputKey to Date.now() in constructor state.
On file upload success , we need to again assign fileInputKey: Date.now(), so it will have different value than previous and it create new file input component on next render()
We can also do this manually by clicking button to clear/reset file Input
Below is the working code :
import React from "react";
import { Button } from "reactstrap";
class FileUpload extends React.Component {
constructor(props) {
super(props);
this.state = {
selectedFile: null,
fileInputKey: Date.now(),
message: ""
};
this.handleClear = this.handleClear.bind(this);
this.onClickHandler = this.onClickHandler.bind(this);
this.onChangeHandler = this.onChangeHandler.bind(this);
}
onChangeHandler = event => {
this.setState({
selectedFile: event.target.files
});
};
onClickHandler = () => {
if (this.state.selectedFile === null) {
this.setState({
message: "Please select File"
});
return;
}
//axios POST req code to send file to server
{
/**
const data = new FormData()
data = this.state.selectedFile[0]
axios.post("http://localhost:8080/api/uploadFile/", data)
.then(res => {
if (res.status == 200) {
// upload success
}
})
.catch(err => {
//message upload failed
})
*/
}
//after upload to server processed
this.setState({
selectedFile: null,
fileInputKey: Date.now(),
message: "File Uploaded"
});
};
handleClear() {
this.setState({
selectedFile: null,
fileInputKey: Date.now(),
message: ""
});
}
render() {
return (
<div>
<input
type="file"
key={this.state.fileInputKey}
class="form-control"
onChange={this.onChangeHandler}
/>
<button
type="button"
class="btn btn-success btn-block"
onClick={this.onClickHandler}
>
Upload
</button>
<Button
type="button"
value="Clear"
data-test="clear"
onClick={this.handleClear}
>
{" "}
Clear{" "}
</Button>
<br />
<label>{this.state.message}</label>
</div>
);
}
}
export default FileUpload;
Here is my solution using redux form
class FileInput extends React.Component {
constructor() {
super();
this.deleteImage = this.deleteImage.bind(this);
}
deleteImage() {
// Just setting input ref value to null did not work well with redux form
// At the same time just calling on change with nothing didn't do the trick
// just using onChange does the change in redux form but if you try selecting
// the same image again it doesn't show in the preview cause the onChange of the
// input is not called since for the input the value is not changing
// but for redux form would be.
this.fileInput.value = null;
this.props.input.onChange();
}
render() {
const { input: { onChange, value }, accept, disabled, error } = this.props;
const { edited } = this.state;
return (
<div className="file-input-expanded">
{/* ref and on change are key properties here */}
<input
className="hidden"
type="file"
onChange={e => onChange(e.target.files[0])}
multiple={false}
accept={accept}
capture
ref={(input) => { this.fileInput = input; }}
disabled={disabled}
/>
{!value ?
{/* Add button */}
<Button
className="btn-link action"
type="button"
text="Add Image"
onPress={() => this.fileInput.click()}
disabled={disabled}
/>
:
<div className="file-input-container">
<div className="flex-row">
{/* Image preview */}
<img src={window.URL.createObjectURL(value)} alt="outbound MMS" />
<div className="flex-col mg-l-20">
{/* This button does de replacing */}
<Button
type="button"
className="btn-link mg-b-10"
text="Change Image"
onPress={() => this.fileInput.click()}
disabled={disabled}
/>
{/* This button is the one that does de deleting */}
<Button
type="button"
className="btn-link delete"
text="Delete Image"
onPress={this.deleteImage}
disabled={disabled}
/>
</div>
</div>
{error &&
<div className="error-message"> {error}</div>
}
</div>
}
</div>
);
}
}
FileInput.propTypes = {
input: object.isRequired,
accept: string,
disabled: bool,
error: string
};
FileInput.defaultProps = {
accept: '*',
};
export default FileInput;
In my case I had a functional component and after selecting a file it suppose to set the file name in the state so using any solution above was failing except the ref one which i fixed like this.
const fileUpload = props => {
const inputEl = useRef(null)
const onUpload = useCallback(e => {
uploadFile(fileDetails)
.then(res => {
inputEl.current.value = ''
})
.catch(err => {
inputEl.current.value = ''
})
})
return (
<input type='file' ref={inputEl} onChange={handleChange} />
<Button onClick={onUpload}>Upload</Button>
)
}
I recently got stumbled into this issue to reset the File type input field. I think it is still a milestone for most developers. So I thought I should share my solution.
Since we are listening to the onChange event to update the image file into some of our states, we will have our component rerendered once we set the state. In such case, we can specify the value of the input file as empty like value='' which will cause the input field to reset its value after each change of its value.
<input
type="file"
value=''
onChange={onChangeFnc}
/>