how to use clearInteval let timer clear it self in ReactJS? - javascript

I am new to react, I am trying to write a react component, component has several features.
user can input a random number, then number will be displayed in the
page too.
implement a button with text value 'start', once click the button,
the number value displayed will reduce one every 1second and the
text value will become 'stop'.
continue click button, minus one will stop and text value of button
will become back to 'start'.
when number subtracted down to 0 will automatically stop itself.
I have implemented first three features. but I am not sure how do I start the last one. should I set another clearInteval? based on if statement when timer counts down 0?
code is here:
var myTimer;
class App extends Component {
constructor(props) {
super(props);
this.state = {
details: [{ id: 1, number: "" }],
type: false
};
this.handleClick = this.handleClick.bind(this);
}
changeNumber = (e, target) => {
this.setState({
details: this.state.details.map(detail => {
if (detail.id === target.id) {
detail.number = e.target.value;
}
return detail;
})
});
};
handleClick = () => {
this.setState(prevState => ({
type: !prevState.type
}));
if (this.state.type === false) {
myTimer = setInterval(
() =>
this.setState({
details: this.state.details.map(detail => {
if (detail.id) {
detail.number = parseInt(detail.number) - 1;
}
return detail;
})
}),
1000
);
}
if (this.state.type === true) {
clearInterval(myTimer);
}
};
render() {
return (
<div>
{this.state.details.map(detail => {
return (
<div key={detail.id}>
Number:{detail.number}
<input
type="number"
onChange={e => this.changeNumber(e, detail)}
value={detail.number}
/>
<input
type="button"
onClick={() => this.handleClick()}
value={this.state.type ? "stop" : "start"}
/>
</div>
);
})}
</div>
);
}
}
export default App;

just add
if (detail.number === 0) {
clearInterval(myTimer);
}
in
handleClick = () => {
this.setState(prevState => ({
type: !prevState.type
}));
if (this.state.type === false) {
myTimer = setInterval(
() =>
this.setState({
details: this.state.details.map(detail => {
if (detail.id) {
detail.number = parseInt(detail.number) - 1;
if (detail.number === 0) {
clearInterval(myTimer);
}
}
return detail;
})
}),
1000
);
}
if (this.state.type === true) {
clearInterval(myTimer);
}
};
Here You have this solution on Hooks :)
const Test2 = () => {
const [on, setOn] = useState(false)
const initialDetails = [{ id: 1, number: "" }]
const [details, setDetails] = useState(initialDetails)
const changeNumber = (e, target) => {
setDetails({ details: details.map(detail => { if (detail.id === target.id) { detail.number = e.target.value; } return detail; }) });
if (this.state.details.number === 0) { setOn(false) }
};
const handleClick = () => {
if (on === false) {myTimer = setInterval(() =>
setDetails({details: details.map(detail => {if (detail.id) {detail.number = parseInt(detail.number) - 1; if (detail.number === 0) {clearInterval(myTimer);} }
return detail;})}),1000);}
if (on === true) { clearInterval(myTimer); }
};
return (
<div>
{details.map(detail => {
return (
<div key={detail.id}>
Number:{detail.number}
<input
type="number"
onChange={e => changeNumber(e, detail)}
value={detail.number}
/>
<input
type="button"
onClick={() => handleClick()}
value={on ? "stop" : "start"}
/>
</div>
);
})}
</div>
)
}

Related

How i can realise button on react for todolist?

I'm new to the react and I would like to know I made a function that makes filtering depending on the boolean values
let [filterState, setFilterSTate] = useState("all");
let filteredUser = todos.filter((е) => {
if (filterState === "active") {
return !е.done;
}
if (filterState === "completed") {
return е.done;
}
return true;
});
in the console, everything is displayed, but I can't figure out how to make it work in the browser itself and only the necessary tricks are displayed
import React, { useState } from "react";
import ReactDOM from "react-dom";
import "./index.css";
const TodoList = () => {
const [newTodo, setNewTodo] = useState("");
const [todos, setTodos] = useState([
{ done: true, text: "Hey", id: 1 },
{ done: false, text: "There", id: 2 },
{ done: false, text: "Dima", id: 3 },
]);
const [id, setId] = useState(4);
const toggleDone = (id) => {
setTodos(
todos.map((todo) => ({
...todo,
done: id === todo.id ? !todo.done : todo.done,
}))
);
};
const updateTodo = (id, e) => {
setTodos(
todos.map((todo) => ({
...todo,
text: id === todo.id ? e.target.value : todo.text,
}))
);
};
const onDelete = (id) => {
setTodos(todos.filter((todo) => todo.id !== id));
};
const updateNewTodo = (e) => {
setNewTodo(e.target.value);
};
const onAdd = () => {
setTodos([
...todos,
{
text: newTodo,
done: false,
id,
},
]);
setId(id + 1);
setNewTodo("");
};
let [filterState, setFilterSTate] = useState("all");
let filteredUser = todos.filter((е) => {
if (filterState === "active") {
return !е.done;
}
if (filterState === "completed") {
return е.done;
}
return true;
});
// let filteredComplited = todos.filter(function (e) {
// return e.done === true;
// });
// console.log("filteredComplited", filteredComplited);
// let filteredActive = todos.filter(function (e) {
// return e.done === false;
// });
// console.log("filteredActive", filteredActive);
// let filteredAll = todos;
// console.log("filteredAll", filteredAll);
return (
<div className="todos">
Todo List
{todos.map((todo) => (
<div key={todo.id} className="todo">
<input
type="checkbox"
value={todo.done}
onChange={() => toggleDone(todo.id)}
/>
<input
type="text"
value={todo.text}
onChange={(evt) => updateTodo(todo.id, evt)}
/>
<button onClick={() => onDelete(todo.id)}>Delete</button>
</div>
))}
<div className="todo">
<input type="text" value={newTodo} onChange={updateNewTodo} />
<button onClick={() => onAdd()}>Add</button>
</div>
<button
onclick={() =>
todos.map((t) => `${t.done ? "x" : ""} ${t.text}`).join("\n")
}
>
Save
</button>
<button onClick={() => setFilterSTate("completed")}>Complited</button>
<button onClick={() => setFilterSTate("active")}>Active</button>
<button onClick={() => setFilterSTate("all")}>All</button>
<pre>filterState: {JSON.stringify(filterState, null, 2)}</pre>
<br />
<pre>{JSON.stringify(filteredUser, null, 2)}</pre>
</div>
);
};
ReactDOM.render(<TodoList />, document.getElementById("todos"));

I have a stopwatch in reactjs, how can I add each number into some sort of array to show each number?

I created a stopwatch using react. My stopwatch starts from 0 and stops at the press of the space button with componenDidMount and componentWillMount. My issue is, I can't seem to figure out how to create some sort of list with the numbers the stopwatch returns. I've created:
times = () => {
this.setState(previousState => ({
myArray: [...previousState.myArray, this.state.milliSecondsElapsed]
}));
};
and then in render() to print it.
<h1>{this.times}</h1>
What I'm trying to do is to create some sort of array that'll keep track of milliSecondsElapsed in my handleStart and handleStop method.
Here's what I have.
import React, {Component} from "react";
import Layout from '../components/MyLayout.js';
export default class Timer extends React.Component {
constructor(props) {
super(props);
this.state = {
milliSecondsElapsed: 0,
timerInProgress: false // state to detect whether timer has started
};
this.updateState = this.updateState.bind(this);
this.textInput = React.createRef();
}
componentDidMount() {
window.addEventListener("keypress", this.keyPress);
}
componentWillUnmount() {
window.removeEventListener("keypress", this.keyPress);
}
textInput = () => {
clearInterval(this.timer);
};
updateState(e) {
this.setState({})
this.setState({ milliSecondsElapsed: e.target.milliSecondsElapsed });
}
keyPress = (e) => {
if (e.keyCode === 32) {
// some logic to assess stop/start of timer
if (this.state.milliSecondsElapsed === 0) {
this.startBtn.click();
} else if (this.state.timerInProgress === false) {
this.startBtn.click();
} else {
this.stopBtn.click();
}
}
};
handleStart = () => {
if (this.state.timerInProgress === true) return;
this.setState({
milliSecondsElapsed: 0
});
this.timer = setInterval(() => {
this.setState(
{
milliSecondsElapsed: this.state.milliSecondsElapsed + 1,
timerInProgress: true
},
() => {
this.stopBtn.focus();
}
);
}, 10);
};
handleStop = () => {
this.setState(
{
timerInProgress: false
},
() => {
clearInterval(this.timer);
this.startBtn.focus();
}
);
};
times = () => {
this.setState(previousState => ({
myArray: [...previousState.myArray, this.state.milliSecondsElapsed]
}));
};
render() {
return (
<Layout>
<div className="index" align='center'>
<input
value={this.state.milliSecondsElapsed/100}
onChange={this.updateState}
ref={this.textInput}
readOnly={true}
/>
<button onClick={this.handleStart} ref={(ref) => (this.startBtn = ref)}>
START
</button>
<button onClick={this.handleStop} ref={(ref) => (this.stopBtn = ref)}>
STOP
</button>
<h1>{this.state.milliSecondsElapsed/100}</h1>
</div>
</Layout>
);
}
}
Issue
this.times is a function that only updates state, it doesn't return any renderable JSX.
times = () => {
this.setState((previousState) => ({
myArray: [...previousState.myArray, this.state.milliSecondsElapsed]
}));
};
Solution
Create a myArray state.
this.state = {
myArray: [], // <-- add initial empty array
milliSecondsElapsed: 0,
timerInProgress: false // state to detect whether timer has started
};
Move the state update logic from this.times to this.handleStop.
handleStop = () => {
this.setState(
(previousState) => ({
timerInProgress: false,
myArray: [
...previousState.myArray, // <-- shallow copy existing data
this.state.milliSecondsElapsed / 100 // <-- add new time
]
}),
() => {
clearInterval(this.timer);
this.startBtn.focus();
}
);
};
Render the array of elapsed times as a comma separated list.
<div>{this.state.myArray.join(", ")}</div>
Full code
class Timer extends React.Component {
constructor(props) {
super(props);
this.state = {
myArray: [],
milliSecondsElapsed: 0,
timerInProgress: false // state to detect whether timer has started
};
this.updateState = this.updateState.bind(this);
this.textInput = React.createRef();
}
componentDidMount() {
window.addEventListener("keypress", this.keyPress);
}
componentWillUnmount() {
window.removeEventListener("keypress", this.keyPress);
}
textInput = () => {
clearInterval(this.timer);
};
updateState(e) {
this.setState({ milliSecondsElapsed: e.target.milliSecondsElapsed });
}
keyPress = (e) => {
if (e.keyCode === 32) {
// some logic to assess stop/start of timer
if (this.state.milliSecondsElapsed === 0) {
this.startBtn.click();
} else if (this.state.timerInProgress === false) {
this.startBtn.click();
} else {
this.stopBtn.click();
}
}
};
handleStart = () => {
if (this.state.timerInProgress === true) return;
this.setState({
milliSecondsElapsed: 0
});
this.timer = setInterval(() => {
this.setState(
{
milliSecondsElapsed: this.state.milliSecondsElapsed + 1,
timerInProgress: true
},
() => {
this.stopBtn.focus();
}
);
}, 10);
};
handleStop = () => {
this.setState(
(previousState) => ({
timerInProgress: false,
myArray: [
...previousState.myArray,
this.state.milliSecondsElapsed / 100
]
}),
() => {
clearInterval(this.timer);
this.startBtn.focus();
}
);
};
render() {
return (
<div>
<div className="index" align="center">
<input
value={this.state.milliSecondsElapsed / 100}
onChange={this.updateState}
ref={this.textInput}
readOnly={true}
/>
<button
onClick={this.handleStart}
ref={(ref) => (this.startBtn = ref)}
>
START
</button>
<button onClick={this.handleStop} ref={(ref) => (this.stopBtn = ref)}>
STOP
</button>
<h1>{this.state.milliSecondsElapsed / 100}</h1>
</div>
<div>{this.state.myArray.join(", ")}</div>
</div>
);
}
}

How to trigger event when timer count downs to 0

I have a parent component which has timer component inside it. Timer starts at 15 minutes and count downs till 0. When my timer shows time as 0 I want to trigger a submit button event, submit button is inside Quiz Component (Quiz Component is also a child component of Parent Component). I found probably I can use MutationObserver when p tag changes. I am not sure whether it's the correct and only approach or there is better way to achieve this.
Parent Component:
import React, { Component } from 'react';
import '../css/App.css'
import Quiz from './Quiz';
import Timer from './Timer';
import { connect } from 'react-redux';
import { ActionTypes } from '../redux/constants/actionTypes';
import { saveQuizAll, getQuizIndex } from '../commonjs/common.js';
const mapStateToProps = state => { return { ...state.quiz, ...state.quizAll } };
const mapDispatchToProps = dispatch => ({
onQuizLoad: payload => dispatch({ type: ActionTypes.QuizLoad, payload }),
onQuizChange: payload => dispatch({ type: ActionTypes.QuizAnswerAll, payload }),
onPagerUpdate: payload => dispatch({ type: ActionTypes.PagerUpdate, payload })
});
class QuizContainer extends Component {
state = {
quizes: [
{ id: 'data/class1.json', name: 'Class 1' },
{ id: 'data/class2.json', name: 'Class 2' },
{ id: 'data/class3.json', name: 'Class 3' },
{ id: 'data/class4.json', name: 'Class 4' },
],
quizId: 'data/class1.json'
};
pager = {
index: 0,
size: 1,
count: 1
}
componentDidMount() {
console.log('componentDidMount');
this.load(this.state.quizId);
}
load(quizId, isValReload) {
console.log('In load');
let url = quizId || this.props.quizId;
if (isValReload) {
let quiz = this.props.quizAll.find(a => url.indexOf(`${a.id}.`) !== -1);
console.log('In load quiz : ', quiz);
this.pager.count = quiz.questions.length / this.pager.size;
this.props.onQuizLoad(quiz);
this.props.onPagerUpdate(this.pager);
}
else {
fetch(`../${url}`).then(res => res.json()).then(res => {
let quiz = res;
quiz.questions.forEach(q => {
q.options.forEach(o => o.selected = false);
});
quiz.config = Object.assign(this.props.quiz.config || {}, quiz.config);
this.pager.count = quiz.questions.length / this.pager.size;
this.props.onQuizLoad(quiz);
this.props.onPagerUpdate(this.pager);
});
}
}
//This event implements restriction to change class without finishing curretnly selectd class
onClassClick = (e) => {
let qus = this.props.quiz.questions;
// console.log(qus);
let isNotAllAns = qus.some((q, i) => {
var isNot = false;
if (q.answerType.id !== 3 && q.answerType.id !== 4) {
isNot = (q.options.find((o) => o.selected === true)) === undefined;
}
else {
// console.log('q', q);
isNot = ((q.answers === "" || q.answers.length === 0));
}
return isNot;
});
if (isNotAllAns) {
alert('Please complete the quiz.');
e.stopPropagation();
}
}
/*
saveQuizAll(_quizAll, _quiz) {
let allQuiz = [];
// , _quizAll, _quiz;
// if (true) {
// _quiz = this.quiz;
// _quizAll = this.quizAll;
// }
console.log(this, _quiz, _quizAll);
if (_quiz.questions.length !== 0) {
if (_quizAll.length !== undefined) {
console.log('Not Initial Setup Splice', _quiz.id);
allQuiz = _quizAll;
const qIndex = this.getQuizIndex(_quiz.id.toString());
if (qIndex > -1) {
allQuiz.splice(qIndex, 1, _quiz);
}
else {
allQuiz.splice(_quizAll.length, 0, _quiz);
// allQuiz.splice(this.props.quizAll.length-1, 0, this.props.quizAll, this.props.quiz);
}
}
else {
allQuiz[0] = _quiz;
}
return allQuiz;
// if (true) {
// this.onQuizChange(allQuiz);
// }
}
}
*/
onChange = (e) => {
// console.log(this.props.quizAll, this.props.quizAll.length);
let allQuiz = [];
allQuiz = saveQuizAll(this.props.quizAll, this.props.quiz);
//below code converted into saveQuizAll funstion
/*
if (this.props.quizAll.length !== undefined) {
console.log('Not Initial Setup Splice', this.props.quiz.id);
allQuiz = this.props.quizAll;
const qIndex = this.getQuizIndex(this.props.quiz.id.toString());
if (qIndex > -1) {
allQuiz.splice(qIndex, 1, this.props.quiz);
}
else {
allQuiz.splice(this.props.quizAll.length, 0, this.props.quiz);
// allQuiz.splice(this.props.quizAll.length-1, 0, this.props.quizAll, this.props.quiz);
}
}
else {
allQuiz[0] = this.props.quiz;
}
*/
// console.log('allQuiz Out - ', allQuiz);
this.props.onQuizChange(allQuiz);
console.log('Check QuizAll - ', this.props.quizAll);
const aQuiz = JSON.parse(JSON.stringify(this.props.quizAll));
this.setState({ quizId: e.target.value });
if (aQuiz.length !== undefined && getQuizIndex(this.props.quizAll, e.target.value) > -1) {
// console.log(aQuiz.findIndex(a => e.target.value.indexOf(`${a.id}.`) !== -1));
this.load(e.target.value, true);
}
else {
this.setState({ quizId: e.target.value });
this.load(e.target.value, false);
}
}
// getQuizIndex(qID) {
// return this.props.quizAll.findIndex(a => (qID.indexOf(`${a.id}.`) !== -1 || qID.indexOf(`${a.id}`) !== -1));
// }
render() {
return (
<div className="container">
<header className="p-2">
<div className="row">
<div className="col-6">
<h3>DADt Application</h3>
</div>
<div className="col-6 text-right">
<label className="mr-1">Select Quiz:</label>
<select onChange={this.onChange} onClick={this.onClassClick}>
{this.state.quizes.map(q => <option key={q.id} value={q.id}>{q.name}</option>)}
</select>
</div>
</div>
</header>
<Timer duration={900}/>
<Quiz quiz={this.state.quiz} quizId={this.state.quizId} saveAll={saveQuizAll} mode={this.state.mode} />
</div>
);
}
}
export default connect(mapStateToProps, mapDispatchToProps)(QuizContainer);
Here is my Timer Component
import React, { Component } from 'react'
class Timer extends Component {
constructor(props) {
super(props);
this.state = {
seconds: 0
};
}
tick() {
this.setState((prevState) => ({
seconds: prevState.seconds + 1
}));
}
componentDidMount() {
this.interval = setInterval(() => this.tick(), 1000);
}
componentWillUnmount() {
clearInterval(this.interval);
}
render() {
const { duration } = this.props;
let timeLeft = duration - this.state.seconds;
timeLeft = Number(timeLeft);
let minutes = Math.floor(timeLeft % 3600 / 60);
let seconds = Math.floor(timeLeft % 3600 % 60);
let minutesDisplay = minutes > 0 ? minutes + (minutes === 1 ? " : " : " : ") : "";
let secondsDisplay = seconds > 0 ? seconds + (seconds === 1 ? "" : "") : "";
return <p className="badge badge-success">Time Left: {minutesDisplay}{secondsDisplay}</p>;
}
}
export default Timer;
Quiz Component:
import React, { Component } from 'react';
import { ActionTypes } from '../redux/constants/actionTypes';
import Review from './Review';
import Questions from './Questions';
import Result from './Result';
import { connect } from 'react-redux';
// import { saveQuizAll } from '../commonjs/common.js';
const mapStateToProps = state => { return { ...state.quiz, ...state.mode, ...state.pager, ...state.quizAll } };
const mapDispatchToProps = dispatch => ({
onSubmit: payload => dispatch({ type: ActionTypes.QuizSubmit, payload }),
onQuizChange: payload => dispatch({ type: ActionTypes.QuizAnswerAll, payload }),
onPagerUpdate: payload => dispatch({ type: ActionTypes.PagerUpdate, payload })
});
class Quiz extends Component {
move = (e) => {
let id = e.target.id;
let index = 0;
if (id === 'first')
index = 0;
else if (id === 'prev')
index = this.props.pager.index - 1;
else if (id === 'next') {
index = this.props.pager.index + 1;
}
else if (id === 'last')
index = this.props.pager.count - 1;
else
index = parseInt(e.target.id, 10);
if (index >= 0 && index < this.props.pager.count) {
let pager = {
index: index,
size: 1,
count: this.props.pager.count
};
this.props.onPagerUpdate(pager);
}
}
saveStore(e) {
let allQuiz = [];
console.log(this, e);
allQuiz = this.props.saveAll(e.props.quizAll, e.props.quiz);
console.log(allQuiz);
this.props.onQuizChange(allQuiz);
}
setMode = (e) => this.props.onSubmit(e.target.id);
// setMode(e) {
// console.log('in mode',e);this.props.onSubmit(e.target.id);
// }
renderMode() {
console.log('Inside here', this.props.mode);
if (this.props.mode === 'quiz') {
return (<Questions move={this.move} />)
} else if (this.props.mode === 'review') {
return (<Review quiz={this.props.quiz} move={this.move} />)
} else {
console.log('Before Results');
const divSel = document.querySelector('div.col-6.text-right');
// console.log('divSel', divSel);
if (divSel) {
divSel.style.display = "none";
}
return (<Result questions={this.props.quizAll || []} />)
}
}
render() {
return (
<div>
{this.renderMode()}
{(this.props.mode !== 'submit') &&
<div>
<hr />
<button id="quiz" className="btn btn-primary" onClick={this.setMode}>Quiz</button>
<button id="review" className="btn btn-primary" onClick={this.setMode}>Review</button>
<button id="submit" className="btn btn-primary" onClick={(e) => {this.setMode(e); this.saveStore(this)}}>Submit Quiz</button >
</div >}
</div>
)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Quiz);
I think you can have two approaches.
1. The "react" way
In the Parent component:
// ...
constructor(props) {
// ...
this.state = {
timeExpired: false
};
}
const onTimeExpired = () => {
this.setState({timeExpired: true});
}
// ...
render() {
return (
<div className="container">
{ // ... }
<Timer duration={900} onTimeExpired={onTimeExpired}/>
<Quiz quiz={this.state.quiz} quizId={this.state.quizId} saveAll={saveQuizAll} mode={this.state.mode} triggerSubmit={this.state.timeExpired} />
</div>
);
}
In the Timer component:
// ...
componentDidUpdate() {
if (this.state.seconds === this.props.duration) {
this.props.onTimeExpired();
}
}
// ...
In the Quiz component:
// ...
componentDidUpdate() {
if (this.props.triggerSubmit) {
// Do whatever you do on submit
}
}
// ...
2. The "quick and dirty" way:
In the Timer component
// ...
componentDidUpdate() {
if (this.state.seconds === this.props.duration) {
const quizForm = document.getElementById('quizFormId');
quizForm && quizForm.submit();
}
}
// ...
Provide a prop method onTimeFinished in your Timer component. Then in your render function you can add
{ !(this.props.duration-this.state.seconds) && this.props.onTimeFinished() }
Reference: React Conditional Rendering
try this:
Parent Component:
// state
state = {
triggerSubmit: false
}
// functions
doSubmit = () => {
this.setState({ triggerSubmit: true });
}
resetSubmit = () => {
this.setState({ triggerSubmit: false });
}
// jsx
<Timer duration={900} doSubmit={this.doSubmit} />
<Quiz
quiz={this.state.quiz}
quizId={this.state.quizId}
saveAll={saveQuizAll}
mode={this.state.mode}
resetSubmit={this.resetSubmit}
triggerSubmit={this.state.triggerSubmit} />
Timer Component:
// function
doSubmit = (timeLeft) => {
if (timeLeft === 0) {
this.props.doSubmit();
}
}
// jsx
<p className="badge badge-success"
onChange={() => {this.doSubmit(timeLeft)}>
Time Left: {minutesDisplay}{secondsDisplay}
</p>
Quiz Component:
// state
state = {
triggerSubmit: this.props.triggerSubmit
}
// function
triggerSubmit = () => {
if (this.state.triggerSubmit) {
your trigger submit code here...
this.props.resetSubmit();
}
}

How to accept more than 1 parameters in onClick React

Hello Im a beginner in React have a button in a react component and I want to pass 2 options to its onClick method, something like:
handleClick = clickType => {
const {currentStep} = this.state
let newStep = currentStep
clickType === 'next' ? newStep++ : newStep--
if (newStep > 0 && newStep <= 6) {
this.setState({
currentStep: newStep
});
}
}
handleChange = input => e => {
this.setState({ [input]: e.target.value });
};
continue = e => {
e.preventDefault();
this.props.nextStep();
};
back = e => {
e.preventDefault();
this.props.prevStep();
};
<button onClick={() => this.handleClick(), this.back} className='previous'>قبلی</button>
<button form='my-form' type='submit' onClick={() => this.handleClick('next'), this.continue} className='next'>ادامه</button>
How can I achieve this correctly?
The arrow function inside the onClick can have execute more than one function.
It is still a function, and you can put code in it ;)
But maybe you can improve your current code :
handleClick = clickType => {
const {currentStep} = this.state
let newStep = currentStep
clickType === 'next' ? newStep++ : newStep--
if (newStep > 0 && newStep <= 6) {
this.setState({
currentStep: newStep
});
}
}
handleChange = input => e => {
this.setState({ [input]: e.target.value });
};
continue = e => {
e.preventDefault();
this.props.nextStep();
};
back = e => {
e.preventDefault();
this.props.prevStep();
};
<button onClick={(e) => { this.handleClick(); this.back(e); }} className='previous'>قبلی</button>
<button form='my-form' type='submit' onClick={() => { this.handleClick('next'); this.continue(e); }} className='next'>ادامه</button>
By this version:
handleNext = (e) => {
const { currentStep } = this.state;
if (currentStep >= 0 && currentStep <= 5) {
this.setState({
currentStep: currentStep++
});
}
this.props.nextStep();
}
handlePrevious = (e) => {
const { currentStep } = this.state;
if (currentStep > 0 && currentStep <= 5) {
this.setState({
currentStep: currentStep--
});
}
this.props.prevStep();
}
<button onClick={this.handlePrevious} className='previous'>قبلی</button>
<button form='my-form' type='submit' onClick={this.handleNext} className='next'>ادامه</button>
You need to setup your handling function differently.
Rather have something like this:
handleBack = e => {
e.preventDefault()
if (this.state.currentStep > 1) {
this.setState((prevState) => ({
currentStep: prevState.currentStep - 1
}));
}
this.props.prevStep()
}
handleNext = e => {
e.preventDefault()
if (this.state.currentStep < 6) {
this.setState((prevState) => ({
currentStep: prevState.currentStep + 1
}));
}
this.props.nextStep()
}
<button onClick={this.handleBack} ... />
<button onClick={this.handleNext} ... />
This method is a lot cleaner and it easier to read because each function deals with its own click.
Now you can easily see exactly what is happening when you click back, and exactly what is happening when you click next.
You can use something like this
/**
I copied this function from code, please make sure that its working.
*/
handleChange = input => e => {
this.setState({ [input]: e.target.value });
};
updateStep = step => {
if (step > 0 && step <= 6)
this.setState({
currentStep: newStep
});
}
/**
Try to avoid the keywords like continue, break, for, while etc as
variable or function names.
*/
handleContinue = e => {
e.preventDefault();
this.handleClick(this.state.currentStep+1);
this.props.nextStep();
};
handleBack = e => {
e.preventDefault();
this.handleClick(this.state.currentStep-1);
this.props.prevStep();
};
<button onClick={this.handleBack} className='previous'>قبلی</button>
<button form='my-form' type='submit' onClick={this.handleContinue} className='next'>ادامه</button>

Trying to set up form validation in my basic todo list app

I am trying to set up validations in my todo list app in react; however this doesn't seem to work. Even if the form is empty the process still goes through and I don't know where the problem is coming from. I am just following a tutorial online since I'm pretty new to this and don't know how to do it myself.
import React from "react";
import * as TodoActions from "../actions/TodoActions";
import TodoStore from "../stores/TodoStore";
import Todo from './Todo.js'
import './Todos.css'
const formValid = ({ formErrors, ...rest }) => {
let valid = true;
Object.values(formErrors).forEach(val => {
val.length > 0 && (valid = false);
});
Object.values(rest).forEach(val => {
val === null && (valid = false);
});
return valid;
};
export default class Todos extends React.Component {
constructor() {
super();
this.state = {
todos: TodoStore.getAll(),
loading: true,
formErrors: {
todo: ""
}
};
TodoActions.receiveTodos()
}
componentWillMount() {
TodoStore.addChangeListener(this.getTodos);
}
componentWillUnmount() {
TodoStore.removeChangeListener(this.getTodos);
}
componentDidUpdate() {
TodoActions.receiveTodos();
}
getTodos = () => {
this.setState({
todos: TodoStore.getAll(),
loading: false
});
}
deleteTodo = (id) => {
TodoActions.deleteTodo(id);
}
addItem = (e) => {
e.preventDefault();
TodoActions.createTodo(this._inputElement.value)
}
handleChange = e => {
e.preventDefault();
const { name, value } = e.target;
let formErrors = { ...this.state.formErrors };
switch (name) {
case "todo":
formErrors.todo =
value.length < 0 ? "Task cannot be empty" : "";
break;
default:
break;
}
this.setState({ formErrors, [name]: value }, () => console.log(this.state));
}
render() {
const { todos } = this.state;
const { formErrors } = this.state;
let TodoComponents;
if (this.state.loading) {
TodoComponents = <h1>Loading...</h1>;
} else if(todos.length) {
TodoComponents = todos.map((todo) => {
return (
<div key={todo.id} className="todo-list">
<Todo key={todo.id} name={todo.name}/>
<div className="todo-btn"><a type="button" onClick={() => this.deleteTodo(todo.id)} className="delete-btn"><i class="fas fa-trash-alt"></i></a></div>
</div>
)
});
} else {
TodoComponents = <p>No tasks to show :)</p>
}
return (
<div className="main-container">
<div className="small-container">
<h1 className="title">All Tasks</h1>
<ul>{TodoComponents}</ul>
<form onSubmit={this.addItem}>
<input ref={(a) => this._inputElement = a} placeholder="Enter Task" className="input-form {formErrors.todo.length < 0 ? 'error' : null}"/>
{formErrors.todo.length < 0 && (
<span className="errorMessage">{formErrors.firstName}</span>
)}
<button type="submit" className="input-btn">Add</button>
</form>
</div>
</div>
);
}
}

Categories