Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[0.7] Problem with removing items from vec in store #3035

Open
mahdi739 opened this issue Sep 27, 2024 · 2 comments
Open

[0.7] Problem with removing items from vec in store #3035

mahdi739 opened this issue Sep 27, 2024 · 2 comments

Comments

@mahdi739
Copy link

mahdi739 commented Sep 27, 2024

Describe the bug
I have a Vec inside a store (notes) and a DateTime inside another store (selected_note_date). I passed the notes to a For and in the for used the selected_note_date to set the selected item.
Now when I change the selected_note_date store and remove a note from the notes, it throws this error:

reading from a keyed field that has not yet been created

To Reproduce

This is the main part:
(Repo: https://github.com/mahdi739/leptos_notes_minirep/blob/main/src/main.rs)

#[derive(Store, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
pub struct State {
  #[store(key: DateTime<Local> = |note| note.date)]
  pub notes: Vec<Note>,
}

#[derive(Store, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
pub struct Note {
  pub title: String,
  pub content: String,
  pub date: DateTime<Local>,
}

#[component]
fn App() -> impl IntoView {
  let state = Store::new(State::default());
  let selected_note_date = Store::new(<Option<DateTime<Local>>>::None);
  let add_notes = move |_| {
    let new_note =
      Note { date: Local::now(), title: "Title".to_string(), content: "Content".to_string() };
    state.notes().update(|it| it.insert(0, new_note.clone()));
    selected_note_date.set(Some(new_note.date));
  };
  let delete_note = move |child: Note| {
    move |event: MouseEvent| {
      event.stop_propagation();
      match state.notes().get().as_slice() {
        [_single_note] => selected_note_date.set(None),
        [.., before_last_note, last_note] if last_note.date == child.date => {
          selected_note_date.set(Some(before_last_note.to_owned().date))
        }
        _ => {
          selected_note_date.set(Some(
            state
              .notes()
              .get()
              .windows(2)
              .find(|window| window[0].date == child.date)
              .map(|window| window[1].to_owned())
              .expect("Coudn't find a good window")
              .date,
          ));
        }
      }
      state.notes().update(|it| it.retain(|item| item.date != child.date));
    }
  };

  view! {
    <div id="main-div">
      <div id="list">
        <button class="add-btn" on:click=add_notes>
          "ADD NEW NOTE"
        </button>
        <ul>
          <For each=move || state.notes() key=move |note| note.date().get() let:child>
            <li
              class="note-item new-item"
              class:selected=move || {
                  selected_note_date.get().as_ref().is_some_and(|it| { it == &child.get().date })// 🔴 Error happens here when getting child after removing a note
              }
              on:click=move |_| selected_note_date.set(Some(child.get().date))
            >
              <div class="items">
                <div class="title">{move || child.title().get()}</div>
                <div class="content">{move || child.content().get()}</div>
              </div>
              <button on:click=delete_note(child.get()) class="fa fa-trash delete-button"></button>
            </li>
          </For>
        </ul>
      </div>
    </div>
  }
}

Additional context
If I don't set the new selected_note_date inside the match, it works.
If I don't remove the item from the notes (inside the retain method), it works.
If I don't get the child in the selected class (class:selected) , it works.
But If I do all three of them, the error happens.

@gbj
Copy link
Collaborator

gbj commented Sep 27, 2024

I added some logging and noticed that the class selectors are being triggered to change before the notes have been modified, so before the each prop is evaluated again.

If I move the two changes in delete_note so that the old note is deleted before the new selected note is set, it also works (?) Or at least does not panic.

Or, if I add something like a request_animation_frame around the selected_note.set so that it evaluates after the notes have been modified, that also works.

I'm not sure if one or the other of those fixes the issue for you. The example is a bit too complicated for me to easily wrap my head around what's actually causing the problem.

@mahdi739
Copy link
Author

mahdi739 commented Sep 27, 2024

Sorry I missed a logical condition. the point of match is "If the deleting child is also selected, select another note" so I should wrap the match with this condition:

if selected_note_date.get().is_some_and(|it| it == child.date) {
      match ...
}

Due to the logic, the deletion should be after match.


And actually wrapping the deleting notes with request_animation_frame did the job, which is wired because it normally should run after setting selected note.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants