I´m trying to do an exercise in React JS "To Do List"
I have to delete some task clicking the trash button, but I don´t know how to do it, because with my code it is deleting all the task.
Could someone help me and explaining me step by step how to do it? I am a programming begginer student
Here is my code
import React from "react";
//include bootstrap npm library into the bundle
import "bootstrap";
import { EventEmitter } from "events";
//create your first component
export class InputToDo extends React.Component {
constructor(props) {
super(props);
this.state = {
input: "",
messages: []
};
this.handleChange = this.handleChange.bind(this);
this.keyPressed = this.keyPressed.bind(this);
this.submitMessage = this.submitMessage.bind(this);
this.deleteTask = this.deleteTask.bind(this);
}
handleChange(event) {
this.setState({ input: event.target.value });
}
keyPressed(event) {
if (event.key === "Enter" && event.target.value !== "") {
this.submitMessage();
event.preventDefault();
}
}
submitMessage() {
this.setState({ messages: [...this.state.messages, this.state.input] });
this.setState({ input: "" });
}
deleteTask() {
this.setState({ messages: [] });
}
render() {
return (
<div className="container">
<h2 className="title">
To Do List
<i className="fas fa-tasks" />
</h2>
<input
className="divInput"
placeholder="What´s next to be done?"
onChange={this.handleChange}
onKeyPress={this.keyPressed}
value={this.state.input}
/>
<ul className="list-group">
{this.state.messages.map((item, i) => (
<li className="list-group-item d-flex" key={i}>
{item}
<i
onClick={this.deleteTask}
className="far fa-trash-alt ml-auto"
/>
</li>
))}
</ul>
</div>
);
}
}
#import "~bootstrap/scss/bootstrap.scss";
.main {
margin: 0px;
}
.container {
border-radius: 10px;
border: solid 2px rgb(80, 75, 75);
min-height: 500px;
width: 400px;
margin: 40px auto;
background-color: rgba(230, 255, 253, 0.987);
padding-bottom: 20px;
}
.title {
padding-top: 10px;
text-align: center;
}
.fas {
padding-left: 20px;
}
.divInput {
position: relative;
display: block;
width: 360px;
height: 50px;
padding-top: 0.75rem;
padding-right: 1.25rem;
padding-bottom: 0.75rem;
padding-left: 1.25rem;
background-color: #fff;
border: 1px solid rgba(0, 0, 0, 0.125);
border-bottom-right-radius: 0.25rem;
border-bottom-left-radius: 0.25rem;
}
.list-group-item {
width: 360px;
}
.far {
color:red;
align-content: right;
cursor: pointer;
}
//import react into the bundle
import React from "react";
import ReactDOM from "react-dom";
//include bootstrap npm library into the bundle
import "bootstrap";
//include your index.scss file into the bundle
import "../styles/index.scss";
//import your own components
import { InputToDo } from "./component/InputToDo.js";
//render your react application
ReactDOM.render(<InputToDo />, document.querySelector("#app"));
First thing you need to do is to pass the index of the element in the deleteTask function call:
<i
onClick={() => this.deleteTask(i)}
className="far fa-trash-alt ml-auto"
/>
Then you can remove the item from the state with the deleteTask function as follows:
deleteTask(i) {
const messages = this.state.messages.filter((_, index) => index !== i)
this.setState({ messages });
}
Related
I know that static site generation idea, The question here is, if SSG gets fetched data at build time, in my next app I used an event handler to get some data according to the search keyword, and it works, How.
This is my code:
Below is the code for the Input component in the next weather app and I use handleSubmit event handler, It gets weather data from an API endpoint.
import React, { useState } from "react";
import PropTypes from "prop-types";
import axios from "axios";
function Input({ onInput }) {
const [inputText, setInputText] = useState();
const [placeholder, setPlaceholder] = useState("أخل اسم البلد أو المنطقة...");
return (
<form className="hstack gap-3" onSubmit={handleSubmit}>
<input
type="text"
className="form-control me-auto"
placeholder={placeholder}
value={inputText || ""}
onChange={handleInput}
onFocus={handleFocus}
onBlur={handleBlur}
/>
<button type="submit">ابحث</button>
<style jsx>{`
input {
height: 50px;
border-radius: 10px;
color: var(--input-color);
font-weight: 500;
font-size: 1.1em;
direction: rtl;
}
input:focus-visible {
color: var(--input-color);
font-weight: 500;
font-size: 1.1em;
}
input:focus {
box-shadow: 0 0 0 0.25rem var(--input-shadow-color);
}
input::placeholder {
color: var(--input-placeholder-color);
font-weight: 500;
opacity: 0.7;
}
button {
padding: 0.60em 1.3em;
background-color: var(--search-btn-background-color);
color: #fff;
font-weight: 500;
border: 5px solid var(--search-btn-border-color);
background-color: var(--search-btn-background-color);
border-radius: 12px;
transition: 0.5s;
}
button:hover {
color: #fff;
background-color: var(--btn-hover-color);
}
button:focus {
box-shadow: 0 0 0 0.25rem var(--search-btn-shadow-color);
}
`}</style>
</form>
);
function handleFocus() {
setPlaceholder("");
}
function handleBlur() {
setPlaceholder("أخل اسم البلد أو المنطقة...");
}
function handleInput(event) {
setInputText(event.target.value);
}
function handleSubmit(event) {
event.preventDefault();
axios
.get(
`https://api.openweathermap.org/data/2.5/weather?q=${inputText}&units=metric&appid=5a60c3e5c70d9f4ad304b2b115b3bf72&lang=ar`
)
.then((response) => {
onInput(response.data);
})
.catch((error) => {
console.log(error);
onInput(0);
});
setInputText("");
}
}
Input.propTypes = {
onInput: PropTypes.func.isRequired,
};
export default Input;
SSG in NextJS only happens for queries inside a getStaticProps function.
In you example, your query is inside the component, so no query is performed at build time, but at run time as usual, so your search feature is working as expected.
List item
I've created a React Iframe (using 'react-frame-component'), and some styled components. When I try to render the styled components inside the React Iframe, the styled components inherits the Parent's (App.css) style, and loses its unique style. Is it possible to maintain the styled component?
Here is my code:
Content.js
import React from 'react';
import ReactDOM from 'react-dom';
import Frame, { FrameContextConsumer } from 'react-frame-component';
import App from "./App";
class Main extends React.Component {
render() {
return (
<Frame head={[<link type="text/css" rel="stylesheet" href={chrome.runtime.getURL("/static/css/content.css")} ></link>]}>
<FrameContextConsumer>
{
({document, window}) => {
return <App document={document} window={window} isExt={true}/>
}
}
</FrameContextConsumer>
</Frame>
)
}
}
App.js
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import Card from './components/Card/index';
class App extends Component {
render() {
return (
<>
<div className="App">
{this.props.isExt ?
<img src={chrome.runtime.getURL("static/media/logo.svg")} className="App-logo" alt="logo" />
:
<img src={logo} className="App-logo" alt="logo" />
}
<Card />
</div>
</>
);
}
}
export default App;
Style:
import React from "react";
import styled, { css } from "styled-components";
export const CardWrapper = styled.div`
overflow: hidden;
padding: 0 0 32px;
margin: 48px auto 0;
width: 300px;
font-family: Quicksand, arial, sans-serif;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.05), 0 0px 40px rgba(0, 0, 0, 0.08);
border-radius: 5px;
`;
export const CardHeader = styled.header`
padding-top: 32px;
padding-bottom: 32px;
`;
export const CardHeading = styled.h1`
font-size: 24px;
font-weight: bold;
text-align: center;
`;
export const CardBody = styled.div`
padding-right: 32px;
padding-left: 32px;
`;
export const CardFieldset = styled.fieldset`
position: relative;
padding: 0;
margin: 0;
border: 0;
& + & {
margin-top: 24px;
}
&:nth-last-of-type(2) {
margin-top: 32px;
}
&:last-of-type {
text-align: center;
}
`;
export const CardInput = styled.input`
padding: 7px 0;
width: 100%;
font-family: inherit;
font-size: 14px;
border-top: 0;
border-right: 0;
border-bottom: 1px solid #ddd;
border-left: 0;
transition: border-bottom-color 0.25s ease-in;
&:focus {
border-bottom-color: #e5195f;
outline: 0;
}
`;
export const CardIcon = styled.span`
color: #666;
cursor: pointer;
opacity: .25;
transition: opacity .25s ease-in;
&:hover {
opacity: .95;
}
${props =>
props.big &&
css`
font-size: 26px;
`}
${props =>
props.eye &&
css`
position: absolute;
top: 8px;
right: 0;
`}
${props =>
props.small &&
css`
font-size: 14px;
`}
`;
export const CardOptionsNote = styled.small`
padding-top: 8px;
display: block;
width: 100%;
font-size: 12px;
text-align: center;
text-transform: uppercase;
`;
export const CardOptions = styled.ul`
padding: 0;
margin: 16px 0 8px;
display: flex;
flex-direction: row;
flex-wrap: wrap;
align-items: center;
justify-content: center;
width: 100%;
list-style-type: none;
`;
export const CardOptionsItem = styled.li`
&:nth-of-type(n + 2) {
margin-left: 16px;
}
`;
export const CardButton = styled.button`
display: block;
width: 100%;
padding: 12px 0;
font-family: inherit;
font-size: 14px;
font-weight: 700;
color: #fff;
background-color: #e5195f;
border: 0;
border-radius: 35px;
box-shadow: 0 10px 10px rgba(0, 0, 0, 0.08);
cursor: pointer;
transition: all 0.25s cubic-bezier(0.02, 0.01, 0.47, 1);
&:hover {
box-shadow: 0 15px 15px rgba(0, 0, 0, 0.16);
transform: translate(0, -5px);
}
`;
export const CardLink = styled.a`
display: inline-block;
font-size: 12px;
text-decoration: none;
color: #aaa;
border-bottom: 1px solid #ddd;
cursor: pointer;
transition: color 0.25s ease-in;
&:hover {
color: #777;
}
`;
Component:
import React from "react";
import {
CardWrapper,
CardHeader,
CardHeading,
CardBody,
CardIcon,
CardFieldset,
CardInput,
CardOptionsItem,
CardOptions,
CardOptionsNote,
CardButton,
CardLink
} from './Card';
const Card = () => {
return (
<>
<CardWrapper>
<CardHeader>
<CardHeading>Sign in</CardHeading>
</CardHeader>
<CardBody>
<CardFieldset>
<CardInput placeholder="Username" type="text" required />
</CardFieldset>
<CardFieldset>
<CardInput placeholder="E-mail" type="text" required />
</CardFieldset>
<CardFieldset>
<CardInput placeholder="Password" type="password" required />
<CardIcon className="fa fa-eye" eye small />
</CardFieldset>
<CardFieldset>
<CardOptionsNote>Or sign up with</CardOptionsNote>
<CardOptions>
<CardOptionsItem>
<CardIcon className="fab fa-google" big />
</CardOptionsItem>
<CardOptionsItem>
<CardIcon className="fab fa-twitter" big />
</CardOptionsItem>
<CardOptionsItem>
<CardIcon className="fab fa-facebook" big />
</CardOptionsItem>
</CardOptions>
</CardFieldset>
<CardFieldset>
<CardButton type="button">Sign Up</CardButton>
</CardFieldset>
<CardFieldset>
<CardLink>I already have an account</CardLink>
</CardFieldset>
</CardBody>
</CardWrapper>
</>
);
};
export default Card;
Attempt at injecting stylesheet for CardWrapper:
/*global chrome*/
/* src/content.js */
import React from 'react';
import { useContext, FrameContext } from 'react';
import ReactDOM from 'react-dom';
import Frame, { FrameContextConsumer } from 'react-frame-component';
import { StyleSheetManager } from 'styled-components';
import App from "./App";
export const StyledFrame = (props) => {
const {
CardWrapper,
CardHeader,
CardHeading,
CardBody,
CardFieldset,
CardInput,
CardIcon,
CardOptionsNote,
CardOptions,
CardOptionsItem,
CardButton,
CardLink,
} = props;
class Main extends React.Component {
render() {
return (
<Frame head={[<link type="text/css" rel="stylesheet" href={chrome.runtime.getURL("/static/css/content.css")} ></link>]}>
<InjectFrameStyles>
{props.CardWrapper}
</InjectFrameStyles>
</Frame>
)
}
}
};
const InjectFrameStyles = (props) => {
const { document } = useContext(FrameContext);
return <StyleSheetManager target={document.head}>{props.CardWrapper}</StyleSheetManager>;
}
const app = document.createElement('div');
app.id = "my-extension-root";
document.body.appendChild(app);
ReactDOM.render(<Main />, app);
app.style.display = "none";
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
if( request.message === "clicked_browser_action") {
toggle();
}
}
);
function toggle(){
if(app.style.display === "none"){
app.style.display = "block";
}else{
app.style.display = "none";
}
}
I had the same problem. I ended up creating a helper component called StyledFrame.
What happens is that your styled-components css is being rendered in the parent frame. You need to intercept those styles, and render them inside your iframe instead. Thats what StyleSheetManager is for.
You can use StyledFrame just like you would the Frame component.
import React, { useContext } from 'react';
import Frame, { FrameContext } from 'react-frame-component';
import { StyleSheetManager } from 'styled-components';
export const StyledFrame = (props) => {
const { style, children, ...otherProps } = props;
return (
<Frame
initialContent={
'<!DOCTYPE html><html><head></head><body><div class="frame-root"></div><div id="modal-root"></div></body></html>'
}
style={{ display: 'block', overflow: 'scroll', border: 0, ...style }}
{...otherProps}
>
<InjectFrameStyles>{props.children}</InjectFrameStyles>
</Frame>
);
};
const InjectFrameStyles = (props) => {
const { document } = useContext(FrameContext);
return <StyleSheetManager target={document.head}>{props.children}</StyleSheetManager>;
};
I'm new to react, and I'm working on a small project that uses a search bar in Single js Web page to find data from API.
The main code for this component is:
const main = () => {
const searchElement = document.querySelector("search-bar");
const clubListElement = document.querySelector("club-list");
const onButtonSearchClicked = async() => {
try{
const result = await DataSource.searchClub(searchElement.value);
renderResult(result);
} catch (message) {
fallbackResult(message)
}
};
const renderResult = (results) => {
clubListElement.clubs = results;
};
const fallbackResult = message => {
clubListElement.renderError(message);
};
searchElement.clickEvent = onButtonSearchClicked;
};
export default main;
also my Search-Bar component:
class SearchBar extends HTMLElement{
constructor(){
super();
this.shadowDOM = this.attachShadow({mode: "open"});
}
connectedCallback(){
this.render();
}
set clickEvent(event){
this._clickEvent = event;
this.render();
}
get value(){
//return this.querySelector("#searchElement").value;
return this.shadowDOM.querySelector("#searchElement").value;
}
render(){
//this.innerHTML = `
this.shadowDOM.innerHTML = `
<style>
.search-container {
max-width: 800px;
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
padding: 16px;
border-radius: 5px;
display: flex;
position: sticky;
top: 10px;
background-color: white;
}
.search-container > input {
width: 75%;
padding: 16px;
border: 0;
border-bottom: 1px solid cornflowerblue;
font-weight: bold;
}
.search-container > input:focus {
outline: 0;
border-bottom: 2px solid cornflowerblue;
}
.search-container > input:focus::placeholder {
font-weight: bold;
}
.search-container > input::placeholder {
color: cornflowerblue;
font-weight: normal;
}
.search-container > button {
width: 23%;
cursor: pointer;
margin-left: auto;
padding: 16px;
background-color: cornflowerblue;
color: white;
border: 0;
text-transform: uppercase;
}
#media screen and (max-width: 550px){
.search-container {
flex-direction: column;
position: static;
}
.search-container > input {
width: 100%;
margin-bottom: 12px;
}
.search-container > button {
width: 100%;
}
}
</style>
<div id="search-container" class="search-container">
<input placeholder="Search football club" id="searchElement" type="search">
<button id="searchButtonElement" type="submit">Search</button>
</div>
`;
this.shadowDOM.querySelector("#searchButtonElement").addEventListener("click", this._clickEvent);
}
}
customElements.define("search-bar", SearchBar);
furthermore, can i convert this method to ReactJS? because we know if we can't declare a const in render() from React.
I have been through a bunch of flustered, and I'm not exactly sure how to go about doing that.
Can anyone help me with this please? Any other comments on the code are also welcome because i'm new in Reacts
Thank you before!
First and foremost, you should remove all that CSS declaration within the render method, and abstract them into a separate CSS file (you can consider CSS modules, which is supported by React), use CSS-in-JavaScript libraries(such as styled-components), or inline styles.
Next, instead of using event listeners, you should bind the button element's onClick event with the onButtonSearchClicked method, which is similar to what you have defined.
From that method, you will make the API request, and update your component's state with the response.
class SearchBar extends React.Component {
async onButtonSearchClicked() {
const result = await DataSource.searchClub(searchElement.value);
// set state with return result.
}
render() {
return (
<div id="search-container" class="search-container">
<input placeholder="Search football club" id="searchElement" type="search">
<button id="searchButtonElement" type="submit" onClick={() => this.onButtonSearchClicked}>Search</button>
</div>
);
}
}
One of many variants how could SearchBar component look like in React
class SearchBar extends React.Component {
constructor() {
this.ref = React.createRef(null);
}
componentDidMount() {
this.shadowDOM = this.ref.current.attachShadow({mode: "open"});
this.shadowDOM.innerHTML = getStyle();
}
onClick(event) {
}
render() {
return <div ref={this.ref} id="search-container" class="search-container">
<input placeholder="Search football club" id="searchElement" type="search">
<button onClick={this.onClick} id="searchButtonElement"
type="submit">Search</button>
</div>
}
I am working on a React application and I am using Redux to store the state. I have the following code:
request.component.jsx:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import Loading from '../loading/loading.component';
import { changeRequestStatus } from '../../redux/requests/requests.actions';
import { RESOLVED, AWAITING_WAIT_STAFF } from '../../redux/requests/requests.status-types'
import './request.styles.scss';
class Request extends Component {
state = { isLoading: false }
render() {
const { _id, table_no, timestamp, description, status } = this.props.request;
const { user, changeRequestStatus } = this.props;
return (
<>
{this.state.isLoading ? <Loading /> : null}
<div className="request-box">
<div className="request-details">
<div>
<h1 style={{ color: status === AWAITING_WAIT_STAFF ? "#28bfa6" : "#f5a953" }}>Table {table_no}, {new Date(timestamp).toLocaleString()}</h1>
<h2>{description}</h2>
</div>
<div className="status-button">
<button
className="request-button"
onClick={async () => {
this.setState({ isLoading: true })
await changeRequestStatus(_id, status === AWAITING_WAIT_STAFF ? user.username : RESOLVED)
this.setState({ isLoading: false })
}} style={{ background: status === AWAITING_WAIT_STAFF ? "linear-gradient(to right, rgba(141,227,227,1) 0%, rgba(114,240,218,1) 100%)" : "linear-gradient(to right, rgba(255,213,94,1) 0%, rgba(246,170,123,1) 100%)" }}>
{status}
</button>
</div>
</div>
</div>
</>
)
}
}
const mapStateToProps = (state) => {
return {
requests: state.requests.requests,
user: state.user.currentUser
}
}
export default connect(mapStateToProps, { changeRequestStatus })(Request);
request.styles.scss:
.request-box {
border: 1px solid #c3c9c8;
height: 200px;
max-width: 100%;
border-radius: 5px;
position: relative;
background-color: white;
font-family: Helvetica;
box-shadow: 0 10px 6px -6px #ededed;
margin: 10px;
}
.request-details {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
padding: 0 30px;
height: 100%;
h1 {
font-size: 30px;
color: #28bfa6;
text-align: left;
}
h2 {
font-size: 22px;
text-align: left;
}
}
.status-button {
padding-bottom: 25px;
width: 100%;
#media (min-width: 1000px) {
width: auto;
padding-right: 20px;
padding-left: 100px;
}
}
.request-button {
height: 50px;
font-size: 19px;
font-weight: 600;
border: none;
border-radius: 5px;
padding: 10px 25px;
background-size: 150% auto;
background: linear-gradient(to right, rgba(141,227,227,1) 0%, rgba(114,240,218,1) 100%);
cursor: pointer;
&:hover {
background: #2de1c2;
}
}
In my Request component, I am changing the background property of my request-button div depending on the value of the status variable.
However, I would like to change the request-buttton:hover property depending on the value of status variable in my Request component.
I am not sure what the correct syntax would be to achieve this. Any insights are appreciated.
Create different CSS classes for each button color/status. Then use a ternary operator to apply the CSS class to the button when status changes. Check the status via Redux and then apply a CSS className like this.
I am new to React.js and trying to create a TODO list. I have two buttons for edit and delete. Delete button is working fine, but not the edit button. I am having a hard time with Edit button. When I click on the edit button, I want the text box to be editable. Then I can edit and when I enter, it should be updated.
I have some css issue with button alignments too. Please help me with this.
App.js
import React from 'react';
import './App.css';
import './AddedTasks.css'
import uuid from 'uuid'
class App extends React.Component{
constructor(props){
super(props);
this.state = {
task:[],
currentTask:{
text:'',
key:''
}
}
this.addTask = this.addTask.bind(this);
this.editTask = this.editTask.bind(this);
this.deleteTask = this.deleteTask.bind(this);
this.handleInput = this.handleInput.bind(this);
}
addTask(event){
event.preventDefault();
const newItem = this.state.currentTask;
if(newItem.text !==""){
const items = [...this.state.task, newItem];
this.setState({
task: items,
currentTask:{
text:'',
key:''
},
})
}
}
editTask(text,key) {
//console.log("items:"+this.state.task);
const items = this.state.task;
items.map(item=>{
if(item.key===key){
//console.log(item.key +" "+key)
// item.text= text.title;
}
})
this.setState({
task: items,
})
}
deleteTask(key){
const currentTaskArray = [...this.state.task]
const taskAfterDeleted = currentTaskArray.filter(deletedTask => deletedTask.key !== key);
this.setState({
task:taskAfterDeleted
})
}
handleInput(event){
this.setState({
currentTask:{
text: event.target.value,
key: uuid()
}
})
}
render(){
return(
<div className='Todo'>
<h1> MyTaskList </h1>
<form id="todo-list" onSubmit={this.addTask}>
<input type="text" className="textInput" placeholder="Enter Item" value={this.state.currentTask.text} onChange={this.handleInput}/>
<button type ="submit">Add</button>
</form>
{this.state.task.map(oneTask=>(
<div className="card">
<div className="container">
<p>{oneTask.text}
<div>
<button className="w3-button delete" onClick={()=> this.deleteTask(oneTask.key)}><i className="fa fa-trash"/></button>
<button className="w3-button edit" onClick={(edit)=>this.editTask(edit.target.value,oneTask.key)}><i className="glyphicon glyphicon-pencil"/></button>
</div>
</p>
</div>
</div>
))}
</div>
);
}
}
export default App;
App.css
_______________
body{
background-color: lightblue;
background-image: url("./todolist.jpg");
background-repeat: no-repeat;
background-size: cover;
alignment: center;
padding-top: 40px;
}
h1{
text-align: center;
color: #bf6318;
padding-right: 17px;
}
.Todo{
background-color: #c1b2cd;
min-height: 500px;
width: 500px;
margin: 150px;
padding-left: 20px;
background-image: url("./pin.jpg");
background-size: 80px;
background-repeat: no-repeat;
}
#todo-list input{
background-color: rgb(95, 83, 135);
border: 0;
width: 250px;
height: 50px;
padding: 0 20px;
margin: 20px;
font-size: 18px;
border-radius: 10px;
color: #ffffff;
}
#todo-list input::placeholder{
color: rgba(255,255,255,0.5);
}
button{
background-color: #008CBA;
border: none;
color: white;
padding: 15px 32px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
margin: 4px 2px;
cursor: pointer;
-webkit-transition-duration: 0.4s; /* Safari */
transition-duration: 0.4s;
box-shadow: 0 12px 16px 0 rgba(0,0,0,0.24),0 17px 50px 0 rgba(0,0,0,0.19);
}
AddedTask.cs
_________________
.card {
box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
transition: 0.3s;
width: 70%;
border-radius: 5px;
margin-bottom: 10px;
margin-left: 20px;
background-color: #bf826b;
}
.card:hover {
box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2);
}
.container {
padding: 4px 16px; /*input/card field*/
height: 40px;
}
.container button.edit{
color: #bf6318;
margin-left: 80px;
margin-right: 10px;
}
.container button.delete{
}
The edit button is not working because you are using edit.target.value which doesn't exist. In my understanding, you are trying to access the text so it will be oneTask.text.
Here is the file:
App.js
import React from "react";
import "./styles.css";
import uuid from "uuid";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
task: [],
currentTask: {
text: "",
key: ""
}
};
this.addTask = this.addTask.bind(this);
this.editTask = this.editTask.bind(this);
this.deleteTask = this.deleteTask.bind(this);
this.handleInput = this.handleInput.bind(this);
}
addTask(event) {
event.preventDefault();
const newItem = this.state.currentTask;
if (newItem.text !== "") {
const items = [...this.state.task, newItem];
this.setState({
task: items,
currentTask: {
text: "",
key: ""
}
});
}
}
editTask(text, key) {
//console.log("items:"+this.state.task);
const items = this.state.task;
this.setState({
task: items.filter(item => item.key !== key),
currentTask: {
text
}
});
}
deleteTask(key) {
const currentTaskArray = [...this.state.task];
const taskAfterDeleted = currentTaskArray.filter(
deletedTask => deletedTask.key !== key
);
this.setState({
task: taskAfterDeleted
});
}
handleInput(event) {
this.setState({
currentTask: {
text: event.target.value,
key:uuid()
}
});
}
render() {
return (
<div className="Todo">
<h1> MyTaskList </h1>
<form id="todo-list" onSubmit={this.addTask}>
<input
type="text"
className="textInput"
placeholder="Enter Item"
value={this.state.currentTask.text}
onChange={this.handleInput}
/>
<button type="submit">Add</button>
</form>
{this.state.task.map(oneTask => (
<div key={oneTask.key} className="card">
<div className="container">
<p>
{oneTask.text}
<div>
<button
className="w3-button delete"
onClick={() => this.deleteTask(oneTask.key)}
>
Delete
</button>
<button
className="w3-button edit"
onClick={() => this.editTask(oneTask.text, oneTask.key)}
>
Edit
</button>
</div>
</p>
</div>
</div>
))}
</div>
);
}
}
export default App;
I have also manipulated the edit task code you can revert back to use your code.
Here is the working link: https://codesandbox.io/s/gifted-almeida-uivq0
Hope this helps!
So I'm assuming that this is a view of todo and by default it's disabled. You can use a state variable to make textboxes enabled when clicking on the edit button.
Add a handler to edit button click event and inside this handler change state variable. So react will enable those two textboxes based on that event.
this.state = {
isEditable:false // by defualt this is disables
}
editHandler(){
//change isEditable to true
}
Make sure to bind this new method in constructor. Then add this to the button click event.
Base on isEditable makes your form controls enabled or disabled.
Regarding CSS, just use a new div element and inside that div include two buttons. So it won't come to on top of text boxes.
<div>
<div>text box and button</div>
<div>second row</div>
<div>edit and delete button></div>
</div>