I have an app, let's call it Stopwatch so you know how it is working.
First, it have second meter which you can pause by pressing stop (stop button appears when pressing start), then you are able to start it again from the paused place or reset and start over.
If i use submit method in the form for saving time value and title, it works fine, but it reset time counter also and make stupid "white flash" after submitting. So i cant use it.
Problem of the case: I made button outside of the form, and it works almost fine. I want clear input with that button also. I tried these inside of the button function, but not working:
document.getElementById("title-value").reset();
document.getElementById("title-value").value = '';
Here is my code (and picture of app below):
import React from 'react';
class StopwatchHistory extends React.Component {
constructor(props) {
super(props);
this.state = {
history: [],
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) { this.setState({value: event.target.value}); }
handleSubmit(event) {
event.preventDefault();
}
componentDidMount() {
this.setHistoryState();
}
setHistoryState = () => {
if (localStorage.times) {
this.setState({ history: localStorage.times.split('|') });
} else {
this.setState({ history: [] });
}
};
saveToLocalStorage = () => {
let titletieto = `${this.state.value}`; // form input value
// printing title and time to appearing list
if (localStorage.times) {
localStorage.times =
`${titletieto} ${this.props.formatTime(
this.props.currentTimeMin
)}:${this.props.formatTime(
this.props.currentTimeSec
)}:${this.props.formatTime(this.props.currentTimeMs, 'ms')}|` +
localStorage.times;
} else {
localStorage.times = `${titletieto} ${this.props.formatTime(
this.props.currentTimeMin
)}:${this.props.formatTime(
this.props.currentTimeSec
)}:${this.props.formatTime(this.props.currentTimeMs, 'ms')}|`;
}
};
saveTime = () => {
if (typeof Storage !== 'undefined') {
this.saveToLocalStorage();
} else {
console.error('local storage not supported');
}
this.setHistoryState();
};
// Remove times from Local Storage
resetHistory = () => {
if (localStorage.times) {
localStorage.removeItem('times');
}
this.setHistoryState();
};
render() {
return (
<div className={'stopwatch__history'}>
<div className="container">
<form id="title-value" onSubmit={this.handleSubmit}>
<label>
<input type="text" value={this.state.value} onChange={this.handleChange} /> </label>
</form>
</div>
<button onClick={this.saveTime}>SAVE TIME</button>
<button onClick={this.resetHistory}>RESET HISTORY</button>
<h3>History</h3>
<ul>
{this.state.history.map((item, index) => <li key={index}>{item}</li>)}
</ul>
</div>
);
}
}
export default StopwatchHistory;
Your input is controlled input. You have value set to be value={this.state.value} and handle change function is:
handleChange(event) { this.setState({value: event.target.value}); }
so if you want to reset this input, your reset function would simply set that state.value to be an empty string.
reset = () => {
this.setState({value: ""});
};
If your input would be uncontrolled (so value and onChange would not be provided) you could change it by accessing it through Ref. (see React.createRef).
Related
I have a simple react flashcard app that sends data to the backend about the flashcard including the question, answer choices, and the answer that the user guessed along with the correct answer. I am trying to post the user's name that they enter into the form to the same backend route as the other data. I have successfully made the form and on submit the program alerts the user that they've entered their username and it displays the username. that works perfectly. Now I'm trying to get that value that was entered for the username and post it to the backend in the same function that I post the other data to the backend so it all gets sent together conveniently on each click. Here is my updated code for my form:
import React from "react"
export default class NameForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.checkGuess = this.checkGuess.bind(this);
}
handleChange = (event) => {
this.setState({value: event.target.value});
}
handleSubmit = (event) => {
alert('A name was submitted: ' + this.state.value);
event.preventDefault();
}
checkGuess() {
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" value={this.state.value} onChange=
{this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
and here is the other component that builds the flashcard and posts the data to the endpoint onClick through the checkGuess function. This already works perfectly without the new username value. :
import React, { useState, useEffect, useRef } from 'react'
import NameForm from './NameForm'
export default function Flashcard({ flashcard }) { // recieving
flashcard
prop from our mapping in flashcardlist.js, each w a unique id
const MAX_TRIES = 4
// const [incorrect, setIncorrect] = useState(incorrect)
const [guess, setGuess] = useState(0)
const [flip, setFlip] = useState(false)
const [height, setHeight] = useState('initial') //sets the state for our
initial height to be replaced by the max height
const frontEl = useRef() // lets us have a reference from the front and
back through every rerendering of them
const backEl = useRef()
// const callDouble = () =>{
// checkGuess();
// postData();
// }
async function postData() {
}
const checkGuess = (answer) => {
try {
console.log(this.state.value)
let result = fetch('http://127.0.0.1:5000/post', {
method: 'POST',
mode: 'no-cors',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
key: `${Date.now()}`,
question: flashcard.question,
answer: flashcard.answer,
options: flashcard.options,
guess: answer,
user: this.state.value
})
});
} catch(e) {
console.log(e)
}
if (answer === flashcard.answer) {
setFlip(true)
return
}
if (guess + 1 === MAX_TRIES) {
setFlip(true)
}
setGuess(guess + 1)
// setIncorrect(true)
}
function setMaxHeight() {
const frontHeight = frontEl.current.getBoundingClientRect().height
//gives us dimensions of the rectangle but we only need the height
const backHeight = backEl.current.getBoundingClientRect().height
setHeight(Math.max(frontHeight, backHeight, 100)) // sets the height
(setHeight) to the maximum height of front or back but the minimum is
100px
}
useEffect(setMaxHeight, [flashcard.question, flashcard.answer,
flashcard.options]) //anytime any of these change then the setMaxHeight
will change
useEffect(() => {
window.addEventListener('resize', setMaxHeight) //everytime we resize
our browser, it sets the max height again
return () => window.removeEventListener('resize', setMaxHeight)
//removes the eventlistener when component destroys itself
}, [])
return (
<div
onClick={() => postData()}
className={`card ${flip ? 'flip' : ''}`} // if flip is true classname
will be card and flip, if flip isnt true it will just be card
style={{ height: height }} //setting height to the variable height
// onClick={() => setFlip(!flip)} // click changes it from flip to non
flip
>
<div className="front" ref={frontEl}>
{flashcard.question}
<div className='flashcard-options'>
{flashcard.options.map(option => {
return <div key={option} onClick={() =>
checkGuess(option)} className='flashcard-option'>{option}</div>
})}
</div>
</div>
<div onClick={() => setFlip(!flip)} className='back' ref={backEl}>
{flashcard.answer}
</div>
</div>
)
}
// setting the front to show the question and the answers by looping
through the options to make them each an option with a class name to style
// back shows the answer
and this is the new error:
TypeError: Cannot read properties of undefined (reading 'state')
at checkGuess (Flashcard.js:34:1)
at onClick (Flashcard.js:92:1)
Since the question has changed a bit based on new code shared by you, please remove the following line:
this.checkGuess = this.checkGuess.bind(this);
Now React components work in a tree structure Root->Children
So you need to figure out whether Flashcard is the root, calling NameForm. Or the other way round.
If Flashcard calls NameForm:
Call NameForm as follows while passing in the checkGuess function:
<NameForm answer={flashcard.answer} checkGuess={checkGuess} />
Then inside NameForm:
handleSubmit = (event) => {
alert('A name was submitted: ' + this.state.value);
this.props.checkGuess(this.props.answer, this.state.value);
event.preventDefault();
}
To support this in Flashcard, change the function signature of checkGuess to:
const checkGuess = (answer, stateValue) => { ... }
And here you use stateValue instead of this.state.value
so i have a basic todo react app and how can i check if the user enters an empty value
How can i also make sure that the empty todo dosent get added to the todolist
Provide us a sample code that you are working with, it will be easy for us to suggest things.
Assuming you using class component. In addTask method you can check if the user has entered the text or not.
export default class App extends React.Component {
state = {
list: [],
task: ""
};
addTask = () => {
const { task, list } = this.state;
if (!task) {
alert("Please enter a task");
return false;
}
this.setState({ list: [...list, task] });
};
render() {
const { task, list } = this.state;
return (
<>
<input
value={task}
onChange={e => this.setState({ task: e.target.value })}
/>
<button onClick={this.addTask}>Add task</button>
<ul>
{list.map(task => (
<li>{task}</li>
))}
</ul>
</>
);
}
}
https://codesandbox.io/s/intelligent-yonath-clken?file=/src/App.js:51-736
I think what you require is similar to below code,
just check if trimmed value is not equal to "", then the trimmed item is added in your list.
function addItem() {
if(inputText.trim()!==""){
setItems(prevItems => {
return [...prevItems, inputText.trim()];
});
setInputText("");
}
}
Also make sure to save the index.js so that server is restarted before trying to access the App on local port.
I am trying to send the contents of the text box to be displayed in the spam when I click on the button
class Hello extends Component {
state = {
texto: ""
}
changeText = () =>{
this.setState({texto: this.state.texto})
}
render() {
return (
<div>
<input type = "text" defaultValue = {this.state.texto}></input>
<p>{this.state.texto}</p>
<button onClick = {this.changeText}>Click</button>
</div>
);
}
}
export default Hello;
function App() {
return (
<div>
<Hello />
</div>
);
}
The idea is that when you click on the button it shows the ones in the text box inside the span.
I am new to react and this clarifies some doubts about the concepts.
thanks guys.
Carlos!
First of all, you must update your input so your code can work properly. In your input, do this instead:
<input type="text" value={this.state.texto} onChange={() => this.onChangeHandler(e.target.value)}></input>
Then, inside your class, you create an onChangeHandler to deal with the data from input first:
onChangeHandler = (e) => {
this.setState({
texto: e
});
}
Ok, we're almost there, now you must create another state item, so you can use it for your final input:
state = {
texto: "",
textoFinished: ""
}
Then, correct your onChange envent:
changeText = () =>{
this.setState({textoFinished: this.state.texto})
}
Now, to access the new value, just go to this.state.textoFinished. Happy coding!
You need to update the state using target value after clicking on it.
Something Like this :
state = {
texto: ""
}
onChangeHandler = (e) => {
const value = e.target.value;
this.setState({texto: value});
}
OR
onChangeHandler = (e) => {
const texto = e.target.value;
this.setState({texto});
}
I am using react-select and just notice that when I already have value in the input field (either by user type or by choosing the option in menu list), then I blur the input then focus at it again - trying to edit my last input - its just start over from the beginning, not continue from the last character of the input. I just found this issue in the author's github. Its been raised from 2 years ago and still an open issue. Is there really no workaround to achieve this?
I recommend you to use controlled props inputValue and value pair with onChange and onInputChange like the following code:
class App extends Component {
constructor(props) {
super(props);
this.state = {
inputValue: "",
value: ""
};
}
onInputChange = (option, { action }) => {
console.log(option, action);
if (action === "input-change") {
const optionLength = option.length;
const inputValue = this.state.inputValue;
const inputValueLength = inputValue.length;
const newInputValue =
optionLength < inputValueLength
? option
: this.state.inputValue + option[option.length - 1];
this.setState({
inputValue: newInputValue
});
}
};
onChange = option => {
this.setState({
value: option
});
};
render() {
return (
<div className="App">
<Select
options={options}
onChange={this.onChange}
onInputChange={this.onInputChange}
inputValue={this.state.inputValue}
value={this.state.value}
/>
</div>
);
}
}
In react-select v1 the props onSelectResetsInput set to false was doing this job but I guess for v2 you will need to do the trick.
Here a live example.
Newbie React question here on show hide functionality.
I have a state of 'show' that I set to false:
this.state = {
show: false,
};
Then I use the following function to toggle
toggleDiv = () => {
const { show } = this.state;
this.setState({ show : !show })
}
And my display is
{this.state.show && <xxxxxx> }
This all works fine. However I want to apply the function it to multiple cases (similar to accordion, without the closing of other children. So I change my constructor to
this.state = {
show: [false,false,false,false,false,false]
};
and this to recognise there are 6 different 'shows'.
{this.state.show[0] && <xxxxxx> }
{this.state.show[1] && <xxxxxx> } etc
But where I get stuck is how to account for them in my toggleDiv function. How do I insert the square bracket reference to the index of show (if this is my problem)?
toggleDiv = () => {
const { show } = this.state;
this.setState({ show : !show })
}
Thanks for looking.
First of all I'd suggest you not to rely on current state in setState function, but to use the callback option to be 100% sure that you are addressing to the newest state:
this.setState((prevState) => ({ show: !prevState.show }));
How to deal with multiple elements?
You'll have to pass the index of currently clicked element.
{yourElements.map((elem, i) => <YourElem onClick={this.toggleDiv(i)} />)}
and then inside your toggleDiv function:
toggleDiv = (i) => () => {
this.setState((prevState) => {
const r = [...prevState.show]; // create a copy to avoid state mutation
r[i] = !prevState.show[i];
return {
show: r,
}
}
}
Use an array instead of a single value. In your toggle div function make a copy of the state array make necessary changes and push the entire array back up to state at the end.
This is some simplified code showing the workflow I described above
export default class myClass extends React.Component{
constructor(props){
super(props);
this.state = { show: new Array(2).fill(false) };
}
//you need a index or id to use this method
toggleDiv = (index) => {
var clone = Object.assign( {}, this.state.show ); //ES6 Clones Object
switch(clone[index]){
case false:
clone[index] = true
break;
case true:
clone[index] = false
break;
}
this.setState({ show: clone });
}
render(){
return(
<div>
{ this.state.show[0] && <div> First Div </div> }
{ this.state.show[1] && <div> Second Div </div> }
{ this.state.show[2] && <div> Third Div </div> }
</div>
)
}
}