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

section 3.5 Iterating over More Complex Data - error in Stores example? #174

Open
gihrig opened this issue Feb 19, 2025 · 2 comments
Open

Comments

@gihrig
Copy link
Contributor

gihrig commented Feb 19, 2025

I've assembled the Stores example as provided in section "3.5 Iterating over More Complex Data Option 4: Stores".

I'm getting an error "no method named value found for struct AtIndex in the current scope".

I get this error at two locations:

error[E0599]: no method named `value` found for struct `AtIndex` in the current scope
  --> src/main.rs:57:22
   |
57 |                 *row.value().write() *= 2;
   |                      ^^^^^ method not found in `AtIndex<KeyedSubfield<Store<Data>, Data, String, ...>, ...>`

error[E0599]: no method named `value` found for struct `AtKeyed` in the current scope
  --> src/main.rs:66:35
   |
66 |                 let value = child.value();
   |                                   ^^^^^ method not found in `AtKeyed<Store<Data>, Data, String, Vec<DatabaseEntry>>`

Not seeing a solution, and having no understanding of Leptos internals, I submitted the issue to Windsurf/Cascade using Claude 3.5 and received this explanation and solution:

Line 57 click handler
Claude (edited):

The error at line 57 is in the click handler where we're trying to update the values. 
for row in data.rows().iter_unkeyed() {
    *row.value().write() *= 2;
}

Claude proposed fix, change the above to:

for row in data.rows().iter_unkeyed() {
    let mut entry = row.write();
    entry.value *= 2;
}

Claude explanation (edited):

The proposed changes fix the issue by:

1. Removing the problematic value() call that was causing the AtIndex error
2. Using write() to get mutable access to the store entry
3. Directly modifying the value field of the entry

Instead of trying to use value() and write() separately, we get a mutable reference to the entire 
entry using write() and then modify its value field. This is the correct way to update values in a 
Leptos store.

Line 66 For loop
Claude: (edited)

The issue is in the handling of child items in the For component.

children=|child| {
    let value = child.value();
    view! { <p>{move || value.get()}</p> }
}

Claude proposed fix, change the above to:

 children=|row| {
     view! { <p>{move || row.read().value}</p> }
 }

Claude explanation (edited):

The error you were seeing ("no method named value found for struct AtIndex") was occurring 
because Leptos was wrapping the data in its internal reactive types, but we were trying to access 
it as if it was the raw data. The AtIndex type is an internal Leptos type used for tracking items in 
a For loop.

The fix properly handles the reactive nature of the data by:

1. Using .read() to get the current value from the store
2. Accessing the value field directly from the DatabaseEntry struct
3. Wrapping it in a closure with `move | |` to ensure it updates reactively

These changes appear to resolve the issues. The code compiles and runs as expected.

Questions:

  1. Do the above issues and changes make sense in terms of Leptos 0.7.7?
  2. Would these changes be appropriate modifications to the book as a PR?

Thank you for reading this!

@gbj
Copy link
Contributor

gbj commented Feb 20, 2025

The example in the book (with the two struct definitions and the code below) compiles. Based on the error message, I wonder whether one of the Store derives was missing in what you tried?

Reading through the Claude conversation here, Claude seems to have changed the meaning of the code here in a pretty significant way—row.value().read() tracks only the value field of the struct, row.read().value tracks the entire row (so, would track changes to either key or value I suppose)

and then lied to you with confidence!

This is the correct way to update values in a Leptos store.

Nope! row.value().write() is definitely a better way to update the value; row.write().value will notify anything that subscribes to any field in the row, row.value().write() will only notify subscribers to the value field.


Working example

#[derive(Store, Debug, Clone)]
pub struct Data {
    #[store(key: String = |row| row.key.clone())]
    rows: Vec<DatabaseEntry>,
}

#[derive(Store, Debug, Clone)]
struct DatabaseEntry {
    key: String,
    value: i32,
}

#[component]
pub fn App() -> impl IntoView {
    // instead of a single with the rows, we create a store for Data
    let data = Store::new(Data {
        rows: vec![
            DatabaseEntry {
                key: "foo".to_string(),
                value: 10,
            },
            DatabaseEntry {
                key: "bar".to_string(),
                value: 20,
            },
            DatabaseEntry {
                key: "baz".to_string(),
                value: 15,
            },
        ],
    });

    view! {
        // when we click, update each row,
        // doubling its value
        <button on:click=move |_| {
            // allows iterating over the entries in an iterable store field
            use reactive_stores::StoreFieldIterator;

            // calling rows() gives us access to the rows
            for row in data.rows().iter_unkeyed() {
                *row.value().write() *= 2;
            }
            // log the new value of the signal
            leptos::logging::log!("{:?}", data.get());
        }>
            "Update Values"
        </button>
        // iterate over the rows and display each value
        <For
            each=move || data.rows()
            key=|row| row.read().key.clone()
            children=|child| {
                let value = child.value();
                view! { <p>{move || value.get()}</p> }
            }
        />
    }
}

@gihrig
Copy link
Contributor Author

gihrig commented Feb 21, 2025

Claude seems to have changed the meaning of the code here in a pretty significant way...

and then lied to you with confidence!

    "This is the correct way to update values in a Leptos store."

Nope! 

To bad LLMs, still not excelling at coding, are becoming better "Automated Bullshit Generators" ;-)


Your hunch "Based on the error message, I wonder whether one of the Store derives was missing in what you tried?"

Turned out to be exactly correct!

I had

#[derive(Debug, Clone)]
struct DatabaseEntry {
    key: String,
    value: i32,
}

That was a tough one for me. The error appeared so far from the real problem and mentioned an actual error outside my code without providing a reference to the actual location...

Thank you ever so much for your help, and for Leptos!

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