Below is my code and I got a feeling I am really doing this wrong. I am new to react and I been spending many hours trying to figure this out with no luck.
I am trying to get users to input values of age, gender, height, weight etc..and then make the BMR box update with a value.
What i have so far is when the user clicks "Calculate for BMR" the onClick function spits out the correct result, but I have no clue how to get the value to appear in the "BMR input box" without any sort of refreshing.
Any help would be appreciated. Thanks
class Calculator extends React.Component {
constructor(props) {
super(props);
this.state = {};
this.handleGenderChange = this.handleGenderChange.bind(this);
this.handleAgeChanged = this.handleAgeChanged.bind(this);
this.handleWeightChanged = this.handleWeightChanged.bind(this);
this.handleFeetChanged= this.handleFeetChanged.bind(this);
this.handleInchesChanged=this.handleInchesChanged.bind(this);
}
handleGenderChange = (event) => {
this.setState({Gender: event.target.value});
}
handleAgeChanged = (event) => {
this.setState({Age: event.target.value});
}
handleWeightChanged = (event) => {
this.setState({Weight: event.target.value});
}
handleHeightChanged = (event) => {
this.setState({Height: event.target.value});
}
handleFeetChanged = (event) => {
this.setState({Feet: event.target.value});
}
handleInchesChanged = (event) => {
this.setState({Inches: event.target.value});
}
onClick= (event) => {
event.preventDefault();
console.log(this.state);
const totalHeight = Number(this.state.Feet) * 12 + Number(this.state.Inches);
if(this.state.Gender == 'Male'){
var BMR = 66 + (6.23 * Number(this.state.Weight)) + (12.7 * totalHeight) - (6.8 * Number(this.state.Age));
console.log(BMR);
}
if(this.state.Gender == 'female'){
var BMR = 655 + (4.35 * Number(this.state.weight)) + (4.7 * totalHeight) - (4.7 * Number(this.state.age));
console.log(BMR);
}
}
render() {
return (
<div>
<Container>
<form>
<h3>Calories/TDEE Calculator</h3>
<div className="form-group">
<select className="form-control" value={this.state.Gender} onChange={this.handleGenderChange}>
<option disabled selected value> -- Gender-- </option>
<option value="Male">Male</option>
<option value="Female">Female</option>
</select>
</div>
<div className="form-group">
<label htmlFor="Age">Age</label>
<input className="form-control"
onChange={this.handleAgeChanged}
type="input"
id="Age"
name="Age"
placeholder="Enter an Age"
value={this.state.Age}
/>
</div>
<div className="form-group">
<label htmlFor="Weight">Weight (lbs)</label>
<input className="form-control"
onChange={this.handleWeightChanged}
type="input"
id="Weight"
name="Weight"
placeholder="Enter Weight"
value={this.state.Weight}
/>
</div>
<div className="form-group">
<label>"Height (Ft/In)"</label>
<input type="input"
name="Feet"
placeholder="Feet"
onChange={this.handleFeetChanged}
value={this.state.Feet}
/>
<input type="input"
name="Inches"
placeholder="Inches"
onChange={this.handleInchesChanged}
value={this.state.Inches}
/>
</div>
<div className="form-group">
<label>BMR</label>
<input className="form-control"
id="BMR"
name="BMR"
value= ""
/>
</div>
<button className="btn btn-lg btn-primary btn-block" onClick={this.onClick.bind(this)}>Click for BMR</button> <br />
</form>
</Container>
</div>
);
}
}
export default Calculator;
EDIT:
Thanks everyone for taking your time to help, it worked :D. I learned from all your replies.
You don't have anything that renders this.state.BMR so it's not too surprising you don't see it anywhere. Some advice: don't use nonbreaking spaces and <br>: that's what CSS is for. Also, don't use bind, use arrow notation to preserve this, there is no reason to use all these bind calls in your constructor.
And then for the actual question: you need to actually render something, so have an element that either shows the button, or shows the BMR value, based on whether you computed it:
class Calculator extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
handleGenderChange(evt) {
// you really should validate this
this.setState({Gender: event.target.value});
}
...
render() {
return <div>
...
<inputelement onChange={evt => this.handleInchesChanged(evt) } />
...
{ this.showButtonOrResult() }
</div>;
}
showButtonOrResult() {
// if the button wasn't clicked yet, then `BMR` will not yet be a state value
if (!this.state.BMR) {
return <button className="..." onClick={evt => this.onClick(evt)>Click for BMR</button>
}
// if it IS a state value, just render it
return <div>Your BMR is: { this.state.BMR }</div>;
}
}
So when your button is clicked, you do what you do, calculate BMR, then setState that, and render() automatically gets called again. Now there is a value to show, and instead of the button, it'll show a div with the result.
Also note that we are absolutely not using bind(this) in the constructor, because that's ridiculous. Properly handle your events with an arrow function so that you get the event, and then pass the event to the correct function, with normal this context.
You need to label your state properties correctly. Your female calculation is going to spit out NaN because you're using {this.state.weight} when you're setting it as 'Weight'
Initialize your state
this.state = {
bmr: ''
};
Set the value of your input
<div className="form-group">
<label>BMR</label>
<input className="form-control"
id="BMR"
name="BMR"
value={this.state.bmr}
/>
</div>
Set the state in your onclick function
onClick = (event) => {
event.preventDefault();
let BMR;
const totalHeight = Number(this.state.Feet) * 12 + Number(this.state.Inches);
if (this.state.Gender === 'Male') {
BMR = 66 + (6.23 * Number(this.state.Weight)) + (12.7 * totalHeight) - (6.8 * Number(this.state.Age));
this.setState({ bmr: BMR });
} else if (this.state.Gender === 'Female') {
BMR = 655 + (4.35 * Number(this.state.Weight)) + (4.7 * totalHeight) - (4.7 * Number(this.state.Age));
this.setState({bmr: BMR});
}
}
Related
I have an input wuth "ok" button on a page and I want to write a number in my input, then by pressing the button, Input tags should be prepared for me according to the amount of the number I had entered
For example, if I enter the number 4 in my input and then click the OK button, 4 input tags will be created for me.
How can I write this code in react js?
I tried the folloing code but it's not working... .
import {useState} from "react";
const makeInputComponent = () => {
const [numberOfProcess, setNumberOfProcess] = useState(null)
const returnInput = ()=>{
return <input type="text" />
}
const makeInput = () => {
for (let i = 0; i < Number(numberOfProcess); i++) {
returnInput()
console.log(i)
}
}
return (
<div>
<label> enter your number </label>
<input type="text" value={numberOfProcess} onChange={(event)=>setNumberOfProcess(event.target.value)} />
<button onClick={ makeInput } > ok </button>
</div>
)
}
export default makeInputComponent ;
You can try this code.
const [numInputs, setNumInputs] = useState(0)
const createInputs = () => {
const inputArray = []
for (let i = 0; i < numInputs; i++) {
inputArray.push(<input type="text" key={i} />)
}
return inputArray
}
return (
<div>
<input
type="number"
value={numInputs}
onChange={(e) => setNumInputs(+e.target.value)}
/>
<button onClick={createInputs}>OK</button>
{createInputs()}
</div>
)
Solution:
Here is what you can do, take the value from the input and when button is pressed with that input value create a array of that length and then map that array for creating input box.
export default function App() {
const[val,Setval]=useState("")
const[inputbox,Setinputbox]=useState([])
const handleClick=()=>{
const array=new Array(val*1).fill(0)
Setinputbox(array)
}
return (
<div className="App">
<input type="number" value={val} onChange={e=>Setval(e.target.value)}/>
<button onClick={handleClick}>Submit</button>
<div>
{inputbox.map((val,index)=>{
return(
<input key={index} type="text"/>
)
})}
</div>
</div>
);
}
I have a simple countdown component where a user inputs two times and it counts down the seconds between them. The start, stop, and reset work. Except, when I reset the countdown and input two new times (without refreshing the page), I am hit with this error:
TypeError: _this3.start is not a function
> 108 | <button onClick={(e) => this.start()}>Start</button>
Below is my code:
import React, { Component } from 'react';
import './App.css';
class App extends Component {
constructor(){
super();
this.start = this.start.bind(this);
this.toTimestamp = this.toTimestamp.bind(this);
this.getDifference = this.getDifference.bind(this);
this.state = {
input1: '',
input2: '',
countdown: null
}
}
input1ContentChange(e){
const text = e.target.value;
this.setState(()=>{
return {input1: text};
})
}
input2ContentChange(e){
const text = e.target.value;
this.setState(()=>{
return {input2: text};
})
}
toTimestamp(input){
let time = input.split(':');
let seconds = ((+time[0]) * 60 * 60) + ((+time[1]) * 60) + (+time[2]);
return seconds;
}
getDifference(input1, input2){
let difference = (this.toTimestamp(input2))- (this.toTimestamp(input1));
if(this.toTimestamp(input2) < this.toTimestamp(input1)){
alert("please input a later time in Time 2");
}
this.setState({
countdown: difference
})
}
start() {
if(this.state.input1 === '' && this.state.input2 === ''){
alert('please choose 2 times');
}
this.getDifference(this.state.input1, this.state.input2);
this.start = setInterval((e) => {
this.setState((prevState) => {
return {countdown: prevState.countdown - 1};
});
if(this.state.countdown <= 0){
clearInterval(this.start);
}
}, 1000);
}
stop(){
clearInterval(this.start);
}
reset(){
clearInterval(this.start);
this.setState((prevState) => {
return {countdown: null, input1: '', input2:''}
})
}
render() {
return (
<div className="App">
<h1>Countdown Timer</h1>
<p>Please choose two different times below</p>
<div className="input1">
<label>
Time 1:
<input type="time"
step="1"
min= "12:00"
max= "18:00"
value={this.state.input1}
onChange={(e)=> this.input1ContentChange(e)}/>
</label>
</div>
<div className="input2">
<label>
Time 2:
<input type="time"
step="1"
min="12:00"
max="18:00"
value={this.state.input2}
onChange={(e)=> this.input2ContentChange(e)}/>
</label>
</div>
<button onClick={(e) => this.start()}>Start</button>
<button onClick={(e) => this.stop()}>Stop</button>
<button onClick={(e) => this.reset()}>Reset</button>
<h3>{this.state.countdown}</h3>
</div>
);
}
}
export default App;
The error is happening at the start function for restarting the countdown. When I check in my chrome tools using the React extension, the state is managed fine. It seems "this" is getting lost.
You're modifying your class function. When your app loads, your class has a start method, but inside that method you do:
this.start = setInterval(...)
setInterval does not return a function, but an id that you can use later to clear the interval. Even if it did return a function, you probably don't want to be modifying your class methods at runtime.
I would suggest using another variable name:
this.intervalId = setInterval(...)
I can't seem to figure out why my form clears prev data when submitting. I have an edit button that when clicked pops open a form. If I edit the name field but not the birthdate field, the name is changed and the birthdate blanks out. It may be a simple silly error but a 2nd set of eyes may help
class Card extends Component {
constructor(props) {
super(props);
this.state = {
dataEditingMode: false,
planetSelection: this.props.homeWorld,
}
}
onEditDeets() {
this.setState({
dataEditingMode: !this.state.dataEditingMode
});
}
onSaveDeets(element) {
element.preventDefault();
this.props.onSavingEditedDeets(
this.props.id,
this.refs.personName.value,
this.refs.personBirthday.value,
this.refs.personHomeWorld.value)
this.setState({
dataEditingMode: false
});
}
onEditPlanetSelection(event) {
this.setState({
planetSelection:event.target.value
});
}
render() {
let getHomeWorld = (planetID) => {
for (var i = 0; i < this.props.planetList.length; i++) {
if (this.props.planetList[i].id === planetID) {
return this.props.planetList[i].name;
}
}
return 'planet does not exist.'
}
let name = this.props.name;
let imageURL = this.props.imageURL;
let birthday = this.props.birthday;
let homeWorld = this.props.homeWorld;
let dataEditingForm;
if (this.state.dataEditingMode === true) {
dataEditingForm = <form
onSubmit={this.onSaveDeets.bind(this)}>
<span>Name: </span>
<input type="text" ref="personName" />
<span>Birthday: </span>
<input type="text" ref="personBirthday" />
<span>Homeworld: </span>
<select
value={this.state.planetSelection}
ref="personHomeWorld"
onChange={this.onEditPlanetSelection.bind(this)}
>
{this.props.planetList.map((planet)=>{
return <option
key={planet.id}
value={planet.id}
>
{planet.name}
</option>
})}
</select>
<button>Save Deets</button>
</form>
} else {
dataEditingForm = <div></div>
}
return (
<div className='card'>
<div className='card-content'>
<div className='card-name'>{name}</div>
<img src={imageURL} alt='profile'/>
<p>
<span>Birthday:</span>
<span>{birthday}</span>
</p>
<p>
<span>Homeworld:</span>
<span>{getHomeWorld(homeWorld)}</span>
</p>
<p>
<span>
<button type="button" onClick={this.onEditDeets.bind(this)}>Edit Card Deets</button>
</span>
</p>
{dataEditingForm}
</div>
</div>
);
}
}
export default Card;
Basically, you are updating your state based on the values of the form, irrespective of whether they are changed or not.
For a simple change, you can just set the default value of your input tags to the state
<span>Name: </span>
<input type="text" ref="personName" defaultValue="{name}" />
<span>Birthday: </span>
<input type="text" ref="personBirthday" defaultValue="{birthday}"/>
Also, in this case, I prefer performing edits like this based on the form state but depending on the scenario you would want to handle onChange.
For example, in a settings page, you might want certain toggles to be effective immediately. Then you should handle onChange and update the state directly.
my check boxes are not getting checked, when created dynamically. I am not able to find the problem. Though, when I hard-code the values for check box id and label for, it just works.
var category_list = this.props.categories_list.map(function(name, i) {
// debugger
return (
<div className="group-chkbx list-group-item">
<input key={i+11} type="checkbox" id={name.category_id} name="category" />
<label htmlFor={name.category_id}>{name.name}</label>
</div>
)
});
After a lot of research one of my colleague helped me out with a solution. The htmlFor and id must be same, but cannot be only numeric. The Ids that I'm using are purely numeric. When I added alphabet as a prefix, it just started working like charm. Thanks all for showing interest and helping out here.
There's nothing that would set the checked prop on them, anyway. When should they be checked?
(Also, remember that components in arrays (such as what .map returns) should have unique key props.)
If your checkboxes are not getting checked, most probably is that some other functionality is preventing it.
Here and example of how to get the checkbox values:
class WithChecks extends Component {
constructor(props){
super(props);
this.getValue = this.getValue.bind(this);
}
getValue(e){
const chk = e.target;
console.log(chk.checked);
console.log(chk.value);
}
render() {
const arr = ['a', 'b', 'c', 'd'];
return (
<div>
{
arr.map((value, index) => {
return (
<div key={index}>
<input type="checkbox"
id={'chk' + index}
onChange={this.getValue}
name="category"
value={value} />
<label htmlFor={'chk' + index}>{value}</label>
</div>
);
})
}
</div>
);
}
}
Maybe this can help to clarify.
The checked property of the input will control whether it is checked. Usually I use local state (or something from global redux state to control what is checked). Little Example:
class Something extends React.Component {
constructor(props) {
super(props);
this.state = {
checked: 0
}
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
// Do Stuff
}
render() {
return (
<div>
{
this.props.categories_list.map(function(name, i) {
return (
<div className="group-chkbx list-group-item" key={i}>
<input checked={i === this.state.checked} onChange={this.handleChange} type="checkbox" id={name.category_id} name="category" />
<label htmlFor={name.category_id}>{name.name}</label>
</div>
)
});
}
</div>
);
}
}
I'm trying use a input to update the number of items of a list. Right now my code is updating the input value correctly but the array that i'm using to generate the list is updating only if I call the OnBlur event twice. I don't know where I'm failing. I used the OnChange event and the problem is the same.
The function that handles the update logic is UpdateList
Heres the jsBin working http://jsbin.com/favabamitu/edit?html,js,console,output
Here's my code:
var InputData = React.createClass({
getInitialState: function() {
return({
number_of_locations: 4,
thickness_m: []
});
},
componentDidMount: function() {
for (var i = 0; i < this.state.number_of_locations; i++) {
this.state.thickness_m.push(0);
console.log("state has been intitialized");
}
},
UpdateList: function(event) {
var value = event.target.value;
var locations = parseInt(this.state.number_of_locations);
console.log("local locations value is "+ locations);
var thickness = this.state.thickness_m;
if (locations > thickness.length) {
var dif = locations - thickness.length;
for (var i = 0; i < dif; i++) {
thickness.push(0);
}
console.log('up with' + dif + 'dif');
} else if (locations < thickness.length) {
var dif = thickness.length - locations;
thickness.splice(0, dif);
console.log('down with' + dif + 'dif');
}
this.setState({
number_of_locations: value,
thickness_m: thickness
});
},
Lister: function(number, index) {
return (
<li key = {index}> {number}</li>
);
},
render: function() {
var thickness_values = this.state.thickness_m
return (
<div className="row">
<div className="col-md-6">
<div className="component">
<div className="form-group">
<label htmlFor="read-method">Reading Method</label>
<select className="form-control" name="read-method" id="read-method" required>
<option value="--">--</option>
<option value="1">Point Thickness Readings - PTR</option>
<option value="2">Critical Thickness Profiles - CTP</option>
</select>
</div>
<div className="form-group">
<label htmlFor="number-of-locations">Number of Locations</label>
<input onBlur={this.UpdateList}
defaultValue={this.state.number_of_locations}
className="form-control"
type="number"
max="50"
min="1"
id="number-of-locations"/>
</div>
<div className="form-group">
<label htmlFor="separation">Separation</label>
<input className="form-control" type="number" id="separation"/>
</div>
<div className="form-group">
<ul>
{thickness_values.map( this.Lister )}
</ul>
<small>your list now has {this.state.number_of_locations} items</small>
</div>
</div>
</div>
<div className="col-md-6">
hello
</div>
</div>
)
}
})
ReactDOM.render( <InputData /> ,
document.getElementById('input-measurement-data')
);
<div id="input-measurement-data">
<!-- This element's contents will be replaced with your component. -->
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
Any help would be greatly appreciated!
Change this line:
var locations = parseInt(this.state.number_of_locations);
to
var locations = parseInt(value);
For your code it is updated in next onChange, because you are updating this.state.number_of_locations at the end of function UpdateList but calculating number of locations before this update.
BTW, updating this state before locations calculation wouldn't help, because of this React feature:
setState() does not immediately mutate this.state but creates a pending state transition. Accessing this.state after calling this method can potentially return the existing value.