dioxus/packages/hooks/docs/rules_of_hooks.md
Evan Almloff 0127501dbf
Improve inline docs (#2460)
Improve inline docs

* improve incorrect event handler return error message

* Improve event handler docs

* document the eval functions

* document spawn and common spawn errors

* fix event handler docs

* add notes about how you use attributes and elements in rsx

* add doc aliases for attributes and events we rename

* add some more aliases for common search terms

* don't doc ignore any public examples in core

* don't ignore public doc examples in ssr

* don't ignore examples in the dioxus package readme

* add a warning when you launch without a renderer enabled

* fix some outdated element docs

* add a bunch of examples to resource

* add notes about desktop events

* add more docs for use_resource

* add on_unimplemented hint to Dependency

* fix some unresolved links

* add examples to each of the router traits

* add not implemented errors for router traits

* add an example to the routable trait

* expand rsx macro docs

* improve memo docs

* update the dioxus readme

* mention dioxus crate features in the docs

* fix a bunch of doc tests

* fix html doc tests

* fix router doc tests

* fix dioxus signals doc tests

* fix dioxus ssr doc tests

* fix use_future example in the hooks cheat sheet

* add a javascript alias for eval

* fix hook explanation values

* remove unused embed-doc-image dependency
2024-06-06 18:15:17 -07:00

4.3 KiB

Additional Information that may be useful

This function is a hook which means you need to follow the rules of hooks when you call it. You can click here to learn more about the rules of hooks.

Hooks in dioxus need to run in the same order every time you run the component. To make sure you run hooks in a consistent order, you should follow the rules of hooks:

  1. Hooks should only be called from the root of a component or another hook
# use dioxus::prelude::*;
fn App() -> Element {
    // ✅ You can call hooks from the body of a component
    let number = use_signal(|| 1);
    if number() == 1 {
        // ❌ You can run into issues if you can hooks inside other expressions inside your component
        // If number changes from 0 to 1, the order of the hooks will be different and your app may panic
        let string = use_signal(|| "hello world".to_string());
    }

    todo!()
}

fn use_my_hook() -> Signal<i32> {
    // ✅ You can call hooks from the body of other hooks
    let number = use_signal(|| 1);
    // ❌ Again, creating hooks inside expressions inside other hooks can cause issues
    if number() == 1 {
        let string = use_signal(|| "hello world".to_string());
    }

    number
}
  1. Hooks should always start with use_ to make it clear that you need to call them in a consistent order

Because hooks should only be called from the root of a component or another hook, you shouldn't call hooks inside of:

  • Conditionals
# use dioxus::prelude::*;
fn App() -> Element {
    let number = use_signal(|| 1);
    // ❌ Changing the condition will change the order of the hooks
    if number() == 1 {
        let string = use_signal(|| "hello world".to_string());
    }

    // ❌ Changing the value you are matching will change the order of the hooks
    match number() {
        1 => {
            let string = use_signal(|| "hello world".to_string());
        },
        _ => (),
    }

    todo!()
}
  • Loops
# use dioxus::prelude::*;
fn App() -> Element {
    let number = use_signal(|| 1);
    // ❌ Changing the loop will change the order of the hooks
    for i in 0..number() {
        let string = use_signal(|| "hello world".to_string());
    }

    todo!()
}
  • Event Handlers
# use dioxus::prelude::*;
fn App() -> Element {
    rsx! {
        button {
            onclick: move |_| {
                // ❌ Calling the event handler will change the order of the hooks
                use_signal(|| "hello world".to_string());
            },
            "Click me"
        }
    }
}
  • Initialization closures in other hooks
# use dioxus::prelude::*;
fn App() -> Element {
    let number = use_signal(|| {
        // ❌ This closure will only be called when the component is first created. Running the component will change the order of the hooks
        let string = use_signal(|| "hello world".to_string());
        string()
    });

    todo!()
}
Why do hooks need to run in a consistent order?

Hooks need to be run in a consistent order because dioxus stores hooks in a list and uses the order you run hooks in to determine what part of the state belongs to which hook.

Lets look at an example component:

# use dioxus::prelude::*;
fn App() -> Element {
    let number = use_signal(|| 1); // Hook 1
    let string = use_signal(|| "hello world".to_string()); // Hook 2
    let doubled = use_memo(move || number() * 2); // Hook 3

    todo!()
}

When we first create the component, we run the hooks in the order they are defined and store the state in the component in a list.

[
    Box::new(1),
    Box::new("hello world".to_string()),
    Box::new(2),
]

Next time we run the component, we return items from the state list instead of creating state again.

[
    Box::new(1), // Hook 1 returns 1
    Box::new("hello world".to_string()), // Hook 2 returns "hello world"
    Box::new(2), // Hook 3 returns 2
]

The order the hooks are run it must be the same because the order determines which hook gets what state! If you run the hooks in a different order, dioxus may panic because it can't turn the state back into the right type or you may just get the wrong state for your hook.