I am having an issue with my application's state tracking. I have an edit form, and when I click on a ticket to set it as the selected ticket, I pass the selected ticket down to the edit form. However, I think I have an issue with the way my state is being tracked.
I believe the exact issue is here:
function handleClick(ticket) {
setSelectedTicket(ticket);
}
If I edit a ticket then click on any other tickets, the updated info then gets passed to all the other tickets.
import React, { useEffect, useState } from "react";
import styled from "styled-components";
import TicketDetails from "./TicketDetails";
const Container = styled.div`
position: absolute;
left: 50%;
top: 50%;
-webkit-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
background-color: rgba(255, 255, 255, 0.8);
border-radius: 0.25em;
box-shadow: 0 0 0.25em rgba(0, 0, 0, 0.25);
box-sizing: border-box;
left: 50%;
width: 50vw;
height: 50vh;
position: fixed;
top: 50%;
transform: translate(-50%, -50%);
font-size: 1.5rem;
`;
const Gradient = styled.div`
background: black;
height: 2px;
margin: 1rem;
width: 96%;
`;
const H2 = styled.h2`
margin: 1rem;
padding: 0;
text-align: center;
width: 100%;
`;
const TicketContainer = styled.div`
display: flex;
flex-direction: row;
`;
const TicketList = styled.div`
display: flex;
margin-left: 1rem;
flex-direction: column;
height: 38vh;
overflow-y: auto;
width: 50%;
`;
const Ticket = styled.div`
margin-right: 2rem;
font-size: 1.3rem;
cursor: pointer;
`;
const DetailsContainer = styled.div`
align-items: center;
text-align: center;
margin: 5px auto auto auto;
width: 300px;
`;
const MyTickets = ({ user }) => {
const [tickets, setTickets] = useState([]);
const [selectedTicket, setSelectedTicket] = useState(null);
useEffect(() => {
fetch(`/users/${user.id}`)
.then((r) => r.json())
.then((data) => {
if (data.tickets.length > 25) {
setTickets(data.tickets.slice(0, 25));
} else {
setTickets(data.tickets);
}
});
}, []);
function refreshTickets() {
fetch(`/users/${user.id}`)
.then((r) => r.json())
.then((data) => {
if (data.tickets.length > 25) {
setTickets(data.tickets.slice(0, 25));
} else {
setTickets(data.tickets);
}
});
}
function limitChars(string) {
if (string.length > 18) {
return string.slice(0, 18) + "...";
}
return string;
}
function handleClick(ticket) {
setSelectedTicket(ticket);
}
return (
<>
<div className="bg"></div>
<div className="bg bg2"></div>
<div className="bg bg3"></div>
<Container>
<H2>{user.username}'s tickets</H2>
<Gradient></Gradient>
<TicketContainer>
<TicketList>
{tickets.map((ticket, index) => {
return (
<Ticket key={index} onClick={() => handleClick(ticket)}>
{index + 1}: {limitChars(ticket.title)}
</Ticket>
);
})}
</TicketList>
<DetailsContainer>
{selectedTicket ? (
<TicketDetails
ticket={selectedTicket}
refreshTickets={refreshTickets}
/>
) : (
<div>Select a ticket</div>
)}
</DetailsContainer>
</TicketContainer>
</Container>
</>
);
};
export default MyTickets;
Below is how I update the ticket:
import React, { useState } from "react";
import styled from "styled-components";
const Container = styled.div`
user-select: none;
border-radius: 2px;
border: 2px solid transparent;
box-shadow: none;
box-sizing: border-box;
padding: 8px;
margin-bottom: 8px;
`;
const Title = styled.div`
font-size: 1.5rem;
overflow-wrap: break-word;
margin: 0.5rem;
`;
const Gradient = styled.div`
background: black;
height: 2px;
margin: 0.5rem;
`;
const Description = styled.div`
font-size: 1rem;
overflow-wrap: break-word;
word-break: break-all;
margin: 0.5rem;
`;
const ContentContainer = styled.div`
-webkit-box-flex: 1;
flex-grow: 1;
flex-basis: 100%;
display: flex;
flex-direction: column;
align-items: center;
`;
const Input = styled.input`
height: 2rem;
width: 100%;
text-align: center;
`;
const TextArea = styled.textarea`
height: 100px;
width: 98%;
padding: 1%;
border: none;
resize: none;
`;
const ButtonContainer = styled.div`
flex-direction: row;
`;
const EditButton = styled.button`
width: 5rem;
`;
const categories = [
"URGENT",
"Meetings",
"To Do",
"In Progress",
"Needs Review",
];
const Category = styled.div`
margin: 0.5rem;
`;
const TicketDetails = ({ ticket, refreshTickets }) => {
const [edit, setEdit] = useState(false);
const [title, setTitle] = useState(ticket.title);
const [initialTitle, setInitialTitle] = useState(ticket.title);
const [description, setDescription] = useState(ticket.description);
const [descriptionInit, setDescriptionInit] = useState(ticket.description);
const handleSubmit = (e) => {
e.preventDefault();
fetch(`/tickets/${ticket.id}`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ title: title, description: description }),
})
.then((r) => r.json())
.then((d) => {
console.log("updated ticket", d);
setTitle(d.title);
setDescription(d.description);
refreshTickets();
});
setEdit(false);
};
const handleReset = (e) => {
setTitle(initialTitle);
setDescription(descriptionInit);
};
const handleCancel = (e) => {
setTitle(initialTitle);
setDescription(descriptionInit);
setEdit(false);
};
return (
<>
<Category>{categories[ticket.category_id - 1]}</Category>
<Gradient></Gradient>
{edit ? (
<Container
style={{
backgroundColor: "#B1D4E0",
}}
>
<form onSubmit={handleSubmit} onReset={handleReset}>
<Input
type="text"
id="title"
autoComplete="off"
value={title}
onChange={(e) => setTitle(e.target.value)}
/>
<Gradient></Gradient>
<TextArea
type="text"
id="description"
autoComplete="off"
value={description}
onChange={(e) => setDescription(e.target.value)}
/>
<input type="submit" value="Submit" />
<input type="reset" value="Reset" />
<button onClick={handleCancel}>Cancel</button>
</form>
</Container>
) : (
<Container
style={{
backgroundColor: "#B1D4E0",
}}
>
<Title>{ticket.title}</Title>
<Gradient></Gradient>
<ContentContainer>
<Description>{ticket.description}</Description>
</ContentContainer>
<ButtonContainer>
<EditButton onClick={() => setEdit(true)}>Edit</EditButton>
</ButtonContainer>
</Container>
)}
</>
);
};
export default TicketDetails;