Reputation: 23
Users
have many reviews
, and many trails
through reviews
, and vice versa.
Goal:
On a user
profile page, render a list of names of trails
that user
has reviewed, without duplicating any of the trail
names. For example, if a user
has reviewed a trail
more than once, it should only display the trail
name once. If a user
has reviewed a trail
multiple times, and deletes one of those reviews
, the list should still display the trail
name. If all of the user's
reviews
for that trail
have been deleted, the trail
name should be removed from the rendered list.
When a review
is deleted, update frontend user
state so that user.reviews
and user.trails
are accurate, without making a GET
request to do so.
What I've tried:
Create a set
of unique trail
names from user.trails
and then render them in JSX by mapping over them
const uniqTrailNames = [...new Set(user.trails.map((trail) => {
return trail.name
}))]
{(uniqTrailNames.map((name) => {
return <p key={name}>{name}</p>
}))}
This is successful in rendering a unique list of trail
names from the trails
a user
has reviewed. However, I'm struggling to setUser
state correctly when a review
is deleted.
Currently, my handleDelete()
function successfully destroys the review
in the database, and then calls setUser
in order to update user.reviews
and user.trails
, but this is where I'm updating the trails
property incorrectly. Filtering this way removes all user.trails
objects with id's that match the review's
associated trail_id
, so even if the user
had another review
of the trail
, the trail
name doesn't render because the trails
no longer exist in user.trails
. How do I go about removing only a single trail
object when deleting a review
, so that user.trails
remains accurate, if trail
objects are essentially identical? Am I going about this the wrong way? It's part of my project requirement to showcase the full has_many
through
relationship, so that's why I've taken this approach so far.
import { useContext } from 'react'
import { UserContext } from '../contexts/UserContext'
import { TrailsContext } from '../contexts/TrailsContext'
import { NavLink } from 'react-router-dom'
function UserTrailReview({ review }) {
const {user, setUser} = useContext(UserContext)
const {trails, setTrails} = useContext(TrailsContext)
const {
id,
trail_id,
trail_rating,
condition,
content,
formatted_date,
trailname
} = review
const trail = trails.find((trail) => {
return trail.id === trail_id
})
function handleDelete() {
fetch(`/reviews/${id}`, {
method: "DELETE"
})
.then(setUser({
...user,
reviews: user.reviews.filter((review) => {
return review.id !== id
}),
trails: user.trails.filter((trail) => {
return trail.id !== trail_id
})
}))
.then(() => {
trail.reviews = trail.reviews.filter((review) => {
return review.id !== id
})
const updatedTrails = trails.map((t) => {
if (t.id === trail_id) {
return trail
} else {
return t
}
})
setTrails(updatedTrails)
})
}
Upvotes: 0
Views: 33
Reputation: 23
A better way to avoid the duplication issue in the first place is to use the Active Record Query Method "distinct". In your models, wherever you have a has_many, through relationship, use distinct like so:
has_many :trails, -> { distinct }, through: :reviews
Upvotes: 0