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

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>
);
}
}

Related

Variable not displaying on Screen

I've set condition for when a user enters numbers into a text box depending on what the numbers entered start with on screen it should display the name of the card.I seem to be changing the variable name fine but am having issues actually displaying the variable name. I'm wondering whether instead of having the if statements in the CardCheck.js they should maybe be in App.js or CardTypeDisplay.js. Apologies first week on React.
App.js
import "./App.css";
import React from "react";
import CardCheck from "./CardCheck";
import CardTypeDisplay from "./CardTypeDisplay";
class App extends React.Component {
state = {
cardNumber: "",
cardType: "",
};
handleChange = (event) => {
this.setState({ cardNumber: event.target.value });
};
handleClick = () => {
let { cardNumber } = this.state;
let { cardType } = this.state;
this.setState({
cardType: cardType,
cardNumber: "",
});
};
render() {
let { cardNumber } = this.state;
let { cardType } = this.state;
return (
<div className="App">
<h1>Taken Yo Money</h1>
<CardCheck
cardNumber={cardNumber}
handleChange={this.handleChange}
handleClick={this.handleClick}
/>
<CardTypeDisplay cardType={cardType} />
</div>
);
}
}
export default App;
CardCheck.js
function CardCheck(props) {
let { cardNumber, handleChange, handleClick } = props;
let cardType = props;
if (cardNumber.match(/^34/) || cardNumber.match(/^38/)) {
cardType = "AMEX";
console.log(cardType);
} else if (cardNumber.match(/^6011/)) {
cardType = "Discover";
console.log(cardType);
} else if (
cardNumber.match(/^51/) ||
cardNumber.match(/^52/) ||
cardNumber.match(/^53/) ||
cardNumber.match(/^54/) ||
cardNumber.match(/^55/)
) {
cardType = "MasterCard";
console.log(cardType);
} else if (cardNumber.match(/^4/)) {
cardType = "Visa";
console.log(cardType);
}
return (
<div className="TweetInput">
<div className="bar-wrapper"></div>
<textarea onChange={handleChange} value={cardNumber}></textarea>
<footer>
<button onClick={handleClick} value={cardType}>
Enter Card Details
</button>
</footer>
</div>
);
}
export default CardCheck;
CardTypeDisplay.js
function CardTypeDisplay(props) {
let { cardType } = props;
return (
<div className="cardType">
<p> {cardType} </p>
</div>
);
}
export default CardTypeDisplay;
You could use the hook useEffect in CardCheck to perform an action each time that cardNumber changes
Like this
useEffect(() => {
if (cardNumber.match(/^34/) || cardNumber.match(/^38/)) {
cardType = "AMEX";
console.log(cardType);
} else if (cardNumber.match(/^6011/)) {
cardType = "Discover";
console.log(cardType);
} else if (
cardNumber.match(/^51/) ||
cardNumber.match(/^52/) ||
cardNumber.match(/^53/) ||
cardNumber.match(/^54/) ||
cardNumber.match(/^55/)
) {
cardType = "MasterCard";
console.log(cardType);
} else if (cardNumber.match(/^4/)) {
cardType = "Visa";
console.log(cardType);
}
},{cardNumber})
Also you are defining cartType in CardCheck and CardCheck doesn't know that variable. What you could do is pass this value as parameter in handleClick
<button onClick={(e) => handleClick(e,cartType)} >
Enter Card Details
</button>
And in receive it and change the state
handleChange = (event, type) => {
this.setState({ cardNumber: event.target.value, cartType:type });
};

React class component. Todo app. How to store data with localStorage

I'd like to store todo data with localStorage so that it won't disappear after refreshing the page.
I used React class component when started creating.
I've added 'handleFormSubmit' and 'ComponentDidMount' methods.
nothing stores in localStorage when I type todo and choose date.
get an error in ComponentDidMount with
Line 'const result = localData ? JSON.parse(localData) : [];'
:SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON data
how can I set and get items?
It would be really appreciated if I could get help.
I'd like to make this app really work.
import React from "react"
import TodoItem from "./components/TodoItem"
import todosData from "./components/todosData"
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
todos: todosData,
//setTodos: todosData,
newItem: "",
deadline: "",
editing: false
}
this.handleChange = this.handleChange.bind(this)
this.addTodo = this.addTodo.bind(this)
this.updateInput = this.updateInput.bind(this)
this.deleteItem = this.deleteItem.bind(this)
this.updateItem = this.updateItem.bind(this)
this.updateDeadline = this.updateDeadline.bind(this)
this.updateInputDeadline = this.updateInputDeadline.bind(this)
this.editItem = this.editItem.bind(this)
this.handleFormSubmit = this.handleFormSubmit.bind(this)
}
handleChange(id) {
this.setState((prevState) => {
const updatedTodos = prevState.todos.map((todo) => {
if (todo.id === id) {
return { ...todo, completed: !todo.completed };
} else {
return todo;
}
});
return { todos: updatedTodos };
});
}
addTodo(e) {
e.preventDefault();
const newTodo = {
id: this.state.todos.length + 1,
text: this.state.newItem,
completed: false,
deadline: this.state.deadline
}
const newTodos = this.state.todos.concat([newTodo]);
this.setState({
todos: newTodos
})
}
updateInput(value, id) {
this.setState((prevState) => {
const updatedTodos = prevState.todos.map((todo) => {
if(todo.id === id) {
return {...todo, text: value}
}else {
return todo;
}
})
return {todos: updatedTodos}
})
}
updateInputDeadline(value, id) {
this.setState((prevState) => {
const updatedTodos = prevState.todos.map((todo) => {
if(todo.id === id) {
console.log(value, id);
return {...todo, deadline: value}
}else {
return todo;
}
})
return {todos: updatedTodos}
})
}
updateItem(e) {
this.setState({
newItem: e.target.value
})
}
updateDeadline(e) {
this.setState({
deadline: e.target.value
})
}
deleteItem(id){
const filteredItems= this.state.todos.filter(item =>
item.id!==id);
this.setState({
todos: filteredItems
})
}
editItem(id) {
this.setState({
editing: id
})
}
handleFormSubmit() {
const { todo, deadline } = this.state;
localStorage.setItem('todo', JSON.stringify(todo));
localStorage.setItem('deadline', deadline);
};
componentDidMount() {
const localData = localStorage.getItem('todo');
const result = localData ? JSON.parse(localData) : [];
const deadlineData = localStorage.getItem('deadline');
this.setState({ result, deadlineData });
}
render() {
const todoItems = this.state.todos.map
(item =>
<TodoItem
key={item.id}
item={item}
handleChange={this.handleChange}
addTodo={this.addTodo}
deleteItem={this.deleteItem}
updateInput={this.updateInput}
updateInputDeadline={this.updateInputDeadline}
isEdited={this.state.editing === item.id}
editItem={this.editItem}
/>)
return (
<div className="todo-list">
<Timer />
<form onSubmit={this.handleFormSubmit}>
<div className="add-todo">
<label>Add an item...</label>
<input
type="text"
name="todo"
placeholder="Type item here..."
value={this.state.newItem}
onChange={this.updateItem}
/>
</div>
<div className="date">
<label htmlFor="deadline">Deadline</label>
<input
type="date" id="start" name="deadline"
min="2021-01-01"
max="2024-12-31"
value={this.state.deadline}
onChange={this.updateDeadline}
/>
</div>
<button type="submit" onClick={this.addTodo}>Add to the list</button>
</form>
{todoItems.length === 0 ? <p>No items</p> : null}
<div className="todoitems">
{todoItems}
</div>
</div>
)
}
}
export default App
When you press the button, there are two events that you are trying to call - addTodo and handleFormSubmit. Since you are calling e.preventDefault() in addTodo, the submit event is never called. You could do all of the actions you need in one of the methods.
My guess is that you are either trying to JSON.parse an array instead of an object, or the value of todo is undefined. You are trying to get todo out of this.state, but you only have todos in your state, so it might be a typo. The same goes for deadline.
You are doing the setting and getting correctly. You could actually get data from localStorage even when you are first setting the state in constructor. But the componendDidMount approach you tried is also good.
constructor(props) {
super(props)
const cachedTodos = localStorage.getItem("todo")
this.state = {
todos: cachedTodos ?? todosData,
...
}

setState is not updating or re rendering DOM or component

I cant figure out why my Login component is not re-rendering what is on the page. The state is changed to loginShow after I click on a "Log In" button on top of my form.
import React from 'react';
import SignUp from '../SignUp';
import Cards from '../Articles/Cards';
import Login from '../LogIn';
export default class Gecko extends React.Component {
constructor(props) {
super(props);
this.state = { requestedPostsThatWeGotFromGecko: null, }
this.clickMe = this.clickMe.bind(this)
this.state = {
loginStatus: "loginHide",
authType: "signup"
};
this.loginClose = this.loginClose.bind(this);
this.loginOpen = this.loginOpen.bind(this);
}
loginClose(e) {
e.stopPropagation();
this.setState({
loginStatus: "loginHide"
});
}
loginOpen() {
this.setState({
loginStatus: "loginShow"
});
}
clickMe = () => {
const {requestedPostsThatWeGotFromGecko} = this.state;
this.setState({ requestedPostsThatWeGotFromGecko: !requestedPostsThatWeGotFromGecko })
}
/* mainLogIn = () => {
const {loginStatus} = this.state;
this.setState({ loginStatus: 'loginHide' })
} */
render() {
const { requestedPostsThatWeGotFromGecko } = this.state;
const { loginStatus } = this.state;
console.log(loginStatus);
// const { authType } = this.state;
// if (loginStatus === "loginHide") return <SignUp login={() => this.clickMe()} />
// if (requestedPostsThatWeGotFromGecko.props === true) return <Cards />
if (loginStatus !== "loginShow") return (
<div className="gecko">
{requestedPostsThatWeGotFromGecko ? (
<Cards />
): (
<SignUp login={() => this.clickMe()} />
)
}
</div>
);
if (loginStatus === "loginShow") return <Login />
}
}
I believe the issue lies where I am trying to set what is being displayed using the conditional operator at the end of the code above. I have included where I am setting the state and props from my other components in the code below.
const onClick = () => {
this.props.login();
console.log('rich');
}
const mainLogIn = () => {
this.setState({
loginStatus: "loginShow"
});
console.log('rich1');
console.log(this.state);
}
const onClick = () => {
this.props.login();
console.log('rich');
}
const mainSignUp = () => {
this.props.signup();
this.setState({
loginStatus: "loginHide"
});
console.log('rich2');
}

My handleChange method of simple react todoapp is not doing what it needs to do

In the output, Only the default completed values are checked! not able change the checks of tasks.
These are my Java script files
app.js
class App extends Component {
constructor() {
super()
this.state = {
todos: Todosdata
}
this.handleChange = this.handleChange.bind(this)
}
handleChange(id) {
this.setState(prevState => {
const udpated = prevState.todos.map(todo => {
if (todo.id === id) {
todo.completed = !todo.completed
}
return todo
})
return {
todos: udpated
}
})
}
render() {
const todoelements = this.state.todos.map(item => <ToDoItem key={item.id}
todoitem={item}
handleChange={this.handleChange} />)
return (
<div className="App">
< div className="todo-list" >
{todoelements}
</div >
</div>
)
}
}
ToDoItem.js
const ToDoItem = (props) => {
const Afterstyle = {
fontColor: "red",
textDecoration: "line-through"
}
return (
<div className="todo-item">
<input type="checkbox"
checked ={props.todoitem.completed}
onChange ={() => props.handleChange(props.todoitem.id)} />
<p style={props.todoitem.completed ? Afterstyle : null}>{props.todoitem.task}</p>
</div>
)
}
i did console log inside if condition of handle Change method,its printing 2 times.
I am stuck at this for hours please fix this!
You are mutating the todo item instead of creating a new one. Change your handler like that:
handleChange(id) {
this.setState(prevState => {
const udpated = prevState.todos.map(todo => {
if (todo.id === id) {
// if the id matches return a new object
return {...todo, completed: !todo.completed};
}
return todo
});
return {todos: udpated};
}
}
Live Demo:

Changing state of child component from parent with refs

I have a TaskList. I'm trying to display a child component TaskEdit (edit box for the task) several components lower, onClick of a button.
TaskEdit has a state variable hidden, set to true by default. How do I change the state inside the child component from within the child component?
React documentation mentions a forwardRef() function in order to reference the child component, but I haven't any luck so far with it.
Here's my code:
import TaskEdit from '../TaskEdit';
class TasksBase extends React.Component {
constructor(props) {
super(props);
this.state = {
loading: false,
tasks: [],
};
this.markTaskAsCompleted = this.markTaskAsCompleted.bind(this);
this.deleteTask = this.deleteTask.bind(this);
this.incrementDate = this.incrementDate.bind(this);
this.toggleTaskEdit = this.toggleTaskEdit.bind(this);
}
componentDidMount() {
this.onListenForTasks();
}
componentWillUnmount() {
this.unsubscribe();
}
render() {
const { tasks, loading } = this.state;
return (
<div>
{loading && <div>Loading ...</div>}
{tasks ? (
<TaskList tasks={tasks}
handleToggleEdit={this.toggleTaskEdit}
handleMarkCompleted={this.markTaskAsCompleted}
handleDeleteTask={this.deleteTask}
taskEditElement={this.editTaskElement}
/>
):(
<div>There are no tasks ...</div>
)}
</div>
);
}
onListenForTasks() {
this.setState({ loading: true });
this.unsubscribe = this.props.firebase
.tasks()
.orderBy('importance', 'desc')
.orderBy('urgency', 'desc')
.orderBy('created', 'desc')
.onSnapshot(snapshot => {
if (snapshot.size) {
let tasks = [];
snapshot.forEach(doc =>
tasks.push({ ...doc.data(), uid: doc.id }),
);
this.setState({
tasks: tasks,
loading: false
});
} else {
this.setState({ tasks: null, loading: false });
}
});
}
markTaskAsCompleted(task){
// Some code
}
deleteTask(id){
// Some code
}
toggleEditTask(){
this.editTaskElement.current.toggle();
}
}
const Tasks = withFirebase(TasksBase);
const TaskList = ({ tasks, handleToggleEdit, handleMarkCompleted, handleDeleteTask }) => (
<ul className="tasks">
{tasks.map( task => (
<Task key={task.uid}
task={task}
handleToggleEdit={handleToggleEdit}
handleMarkCompleted={handleMarkCompleted}
handleDeleteTask={handleDeleteTask}
/>
))}
</ul>
);
const Task = ({ task, handleToggleEdit, handleMarkCompleted, handleDeleteTask}) => (
(!task.completed && !task.obsolete && !task.waitingForDependencies) && (!task.start || task.start.toMillis() <= Date.now()) &&
<li className="task">
<strong>{task.userId}</strong> {task.name}
{ task.estimate &&
<span>{task.estimate.amount + task.estimate.unit}</span>
}
<div className="icons">
<FontAwesomeIcon icon="edit" onClick={() => handleToggleEdit(task)} />
<FontAwesomeIcon icon="check-circle" onClick={() => handleMarkCompleted(task)} />
<FontAwesomeIcon icon="times-circle" onClick={() => handleDeleteTask(task.uid)}/>
</div>
<TaskEdit task={task} />
</li>
);
const condition = authUser => !!authUser;
export default compose(
withEmailVerification,
withAuthorization(condition),
)(Tasks);
TaskEdit:
import React, { Component } from 'react';
import { withFirebase } from '../Firebase';
const INITIAL_STATE = {
hidden: true,
};
class TaskEditBase extends Component {
constructor(props) {
super(props);
this.state = { ...INITIAL_STATE };
this.handleInputChange = this.handleInputChange.bind(this);
}
handleInputChange(event) {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
console.log("name " + name);
console.log("value " + value);
this.setState({
[name]: value
});
}
toggle(){
this.setState({
hidden: !this.prevState.hidden
})
}
onSumbitTaskEdit = (event) => {
event.preventDefault();
};
render(){
return(
!this.state.hidden &&
<div className="task-edit">
<form onSubmit={this.onSumbitTaskEdit}>
<div>A bunch of inputs</div>
<input type="submit" value="Edit"/>
</form>
</div>
)
}
}
const TaskEdit = withFirebase(TaskEditBase);
export default TaskEdit;

Categories