fix queries
This commit is contained in:
1 parent
2f473f7c97
commit
037a248ad9
294 files changed
+60706
-263
No files matched your search
@@ -0,0 +1,435 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="sidebar-visible no-js light">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>Custom Hooks</title>
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff" />
|
||||
|
||||
<link rel="icon" href="../favicon.svg">
|
||||
<link rel="shortcut icon" href="../favicon.png">
|
||||
<link rel="stylesheet" href="../css/variables.css">
|
||||
<link rel="stylesheet" href="../css/general.css">
|
||||
<link rel="stylesheet" href="../css/chrome.css">
|
||||
<link rel="stylesheet" href="../css/print.css" media="print">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
|
||||
<link rel="stylesheet" href="../fonts/fonts.css">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" href="../highlight.css">
|
||||
<link rel="stylesheet" href="../tomorrow-night.css">
|
||||
<link rel="stylesheet" href="../ayu-highlight.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
|
||||
<!-- MathJax -->
|
||||
<script async type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Provide site root to javascript -->
|
||||
<script type="text/javascript">
|
||||
var path_to_root = "../";
|
||||
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "navy" : "light";
|
||||
</script>
|
||||
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script type="text/javascript">
|
||||
try {
|
||||
var theme = localStorage.getItem('mdbook-theme');
|
||||
var sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script type="text/javascript">
|
||||
var theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
var html = document.querySelector('html');
|
||||
html.classList.remove('no-js')
|
||||
html.classList.remove('light')
|
||||
html.classList.add(theme);
|
||||
html.classList.add('js');
|
||||
</script>
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script type="text/javascript">
|
||||
var html = document.querySelector('html');
|
||||
var sidebar = 'hidden';
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
}
|
||||
html.classList.remove('sidebar-visible');
|
||||
html.classList.add("sidebar-" + sidebar);
|
||||
</script>
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<div class="sidebar-scrollbox">
|
||||
<ol class="chapter"><li class="chapter-item expanded affix "><a href="../index.html">Introduction</a></li><li class="chapter-item expanded "><a href="../getting_started/index.html"><strong aria-hidden="true">1.</strong> Getting Started</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../getting_started/desktop.html"><strong aria-hidden="true">1.1.</strong> Desktop</a></li><li class="chapter-item expanded "><a href="../getting_started/web.html"><strong aria-hidden="true">1.2.</strong> Web</a></li><li class="chapter-item expanded "><a href="../getting_started/ssr.html"><strong aria-hidden="true">1.3.</strong> Server-Side Rendering</a></li><li class="chapter-item expanded "><a href="../getting_started/fullstack.html"><strong aria-hidden="true">1.4.</strong> Fullstack</a></li><li class="chapter-item expanded "><a href="../getting_started/liveview.html"><strong aria-hidden="true">1.5.</strong> Liveview</a></li><li class="chapter-item expanded "><a href="../getting_started/tui.html"><strong aria-hidden="true">1.6.</strong> Terminal UI</a></li><li class="chapter-item expanded "><a href="../getting_started/mobile.html"><strong aria-hidden="true">1.7.</strong> Mobile</a></li><li class="chapter-item expanded "><a href="../getting_started/hot_reload.html"><strong aria-hidden="true">1.8.</strong> Hot Reloading</a></li></ol></li><li class="chapter-item expanded "><a href="../describing_ui/index.html"><strong aria-hidden="true">2.</strong> Describing the UI</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../describing_ui/special_attributes.html"><strong aria-hidden="true">2.1.</strong> Special Attributes</a></li><li class="chapter-item expanded "><a href="../describing_ui/components.html"><strong aria-hidden="true">2.2.</strong> Components</a></li><li class="chapter-item expanded "><a href="../describing_ui/component_props.html"><strong aria-hidden="true">2.3.</strong> Props</a></li><li class="chapter-item expanded "><a href="../describing_ui/component_children.html"><strong aria-hidden="true">2.4.</strong> Component Children</a></li></ol></li><li class="chapter-item expanded "><a href="../interactivity/index.html"><strong aria-hidden="true">3.</strong> Interactivity</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../interactivity/event_handlers.html"><strong aria-hidden="true">3.1.</strong> Event Listeners</a></li><li class="chapter-item expanded "><a href="../interactivity/hooks.html"><strong aria-hidden="true">3.2.</strong> Hooks & Component State</a></li><li class="chapter-item expanded "><a href="../interactivity/user_input.html"><strong aria-hidden="true">3.3.</strong> User Input</a></li><li class="chapter-item expanded "><a href="../interactivity/sharing_state.html"><strong aria-hidden="true">3.4.</strong> Sharing State</a></li><li class="chapter-item expanded "><a href="../interactivity/custom_hooks.html" class="active"><strong aria-hidden="true">3.5.</strong> Custom Hooks</a></li><li class="chapter-item expanded "><a href="../interactivity/dynamic_rendering.html"><strong aria-hidden="true">3.6.</strong> Dynamic Rendering</a></li><li class="chapter-item expanded "><a href="../interactivity/router.html"><strong aria-hidden="true">3.7.</strong> Routing</a></li></ol></li><li class="chapter-item expanded "><a href="../async/index.html"><strong aria-hidden="true">4.</strong> Async</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../async/use_future.html"><strong aria-hidden="true">4.1.</strong> UseFuture</a></li><li class="chapter-item expanded "><a href="../async/use_coroutine.html"><strong aria-hidden="true">4.2.</strong> UseCoroutine</a></li><li class="chapter-item expanded "><a href="../async/spawn.html"><strong aria-hidden="true">4.3.</strong> Spawning Futures</a></li></ol></li><li class="chapter-item expanded "><a href="../best_practices/index.html"><strong aria-hidden="true">5.</strong> Best Practices</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../best_practices/error_handling.html"><strong aria-hidden="true">5.1.</strong> Error Handling</a></li><li class="chapter-item expanded "><a href="../best_practices/antipatterns.html"><strong aria-hidden="true">5.2.</strong> Antipatterns</a></li></ol></li><li class="chapter-item expanded "><a href="../publishing/index.html"><strong aria-hidden="true">6.</strong> Publishing</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../publishing/desktop.html"><strong aria-hidden="true">6.1.</strong> Desktop</a></li><li class="chapter-item expanded "><a href="../publishing/web.html"><strong aria-hidden="true">6.2.</strong> Web</a></li><li class="spacer"></li></ol></li><li class="chapter-item expanded "><a href="../fullstack/index.html"><strong aria-hidden="true">7.</strong> Fullstack</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../fullstack/getting_started.html"><stLine truncated
|
||||
</div>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
<div id="menu-bar-hover-placeholder"></div>
|
||||
<div id="menu-bar" class="menu-bar sticky bordered">
|
||||
<div class="left-buttons">
|
||||
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<i class="fa fa-bars"></i>
|
||||
</button>
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<i class="fa fa-paint-brush"></i>
|
||||
</button>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="light">Light (default)</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||
</ul>
|
||||
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
<button id="language-toggle" class="icon-button" type="button" title="Select language" aria-label="Select language" aria-haspopup="true" aria-expanded="false" aria-controls="language-list">
|
||||
<i class="fa fa-globe"></i>
|
||||
</button>
|
||||
<ul id="language-list" class="language-popup" aria-label="Languages" role="menu">
|
||||
<li role="none"><a href="../../en/interactivity/custom_hooks.html"><button role="menuitem" class="language" id="light">English</button></a></li>
|
||||
<li role="none"><a href="../../pt-br/interactivity/custom_hooks.html"><button role="menuitem" class="language" id="light">Português Brasileiro</button></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title"></h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
||||
<i id="print-button" class="fa fa-print"></i>
|
||||
</a>
|
||||
<a href="https://github.com/DioxusLabs/dioxus/edit/master/docs/guide" title="Git repository" aria-label="Git repository">
|
||||
<i id="git-repository-button" class="fa fa-github"></i>
|
||||
</a>
|
||||
<a href="https://github.com/DioxusLabs/dioxus/edit/master/docs/guide/src/interactivity/custom_hooks.md" title="Suggest an edit" aria-label="Suggest an edit">
|
||||
<i id="git-edit-button" class="fa fa-edit"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="search-wrapper" class="hidden">
|
||||
<form id="searchbar-outer" class="searchbar-outer">
|
||||
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||
</form>
|
||||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script type="text/javascript">
|
||||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1 id="custom-hooks"><a class="header" href="#custom-hooks">Custom Hooks</a></h1>
|
||||
<p>Hooks are a great way to encapsulate business logic. If none of the existing hooks work for your problem, you can write your own.</p>
|
||||
<p>When writing your hook, you can make a function that accepts <code>cx: &ScopeState</code> as a parameter to accept a scope with any Props.</p>
|
||||
<h2 id="composing-hooks"><a class="header" href="#composing-hooks">Composing Hooks</a></h2>
|
||||
<p>To avoid repetition, you can encapsulate business logic based on existing hooks to create a new hook.</p>
|
||||
<p>For example, if many components need to access an <code>AppSettings</code> struct, you can create a "shortcut" hook:</p>
|
||||
<pre><pre class="playground"><code class="language-rust edition2018">
|
||||
<span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>fn use_settings(cx: &ScopeState) -> &UseSharedState<AppSettings> {
|
||||
use_shared_state::<AppSettings>(cx).expect("App settings not provided")
|
||||
}
|
||||
<span class="boring">}
|
||||
</span></code></pre></pre>
|
||||
<p>Or if you want to wrap a hook that persists reloads with the storage API, you can build on top of the use_ref hook to work with mutable state:</p>
|
||||
<pre><pre class="playground"><code class="language-rust edition2018">
|
||||
<span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>use gloo_storage::{LocalStorage, Storage};
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
|
||||
/// A persistent storage hook that can be used to store data across application reloads.
|
||||
#[allow(clippy::needless_return)]
|
||||
pub fn use_persistent<T: Serialize + DeserializeOwned + Default + 'static>(
|
||||
cx: &ScopeState,
|
||||
// A unique key for the storage entry
|
||||
key: impl ToString,
|
||||
// A function that returns the initial value if the storage entry is empty
|
||||
init: impl FnOnce() -> T,
|
||||
) -> &UsePersistent<T> {
|
||||
// Use the use_ref hook to create a mutable state for the storage entry
|
||||
let state = use_ref(cx, move || {
|
||||
// This closure will run when the hook is created
|
||||
let key = key.to_string();
|
||||
let value = LocalStorage::get(key.as_str()).ok().unwrap_or_else(init);
|
||||
StorageEntry { key, value }
|
||||
});
|
||||
|
||||
// Wrap the state in a new struct with a custom API
|
||||
// Note: We use use_hook here so that this hook is easier to use in closures in the rsx. Any values with the same lifetime as the ScopeState can be used in the closure without cloning.
|
||||
cx.use_hook(|| UsePersistent {
|
||||
inner: state.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
struct StorageEntry<T> {
|
||||
key: String,
|
||||
value: T,
|
||||
}
|
||||
|
||||
/// Storage that persists across application reloads
|
||||
pub struct UsePersistent<T: 'static> {
|
||||
inner: UseRef<StorageEntry<T>>,
|
||||
}
|
||||
|
||||
impl<T: Serialize + DeserializeOwned + Clone + 'static> UsePersistent<T> {
|
||||
/// Returns a reference to the value
|
||||
pub fn get(&self) -> T {
|
||||
self.inner.read().value.clone()
|
||||
}
|
||||
|
||||
/// Sets the value
|
||||
pub fn set(&self, value: T) {
|
||||
let mut inner = self.inner.write();
|
||||
// Write the new value to local storage
|
||||
LocalStorage::set(inner.key.as_str(), &value);
|
||||
inner.value = value;
|
||||
}
|
||||
}
|
||||
<span class="boring">}
|
||||
</span></code></pre></pre>
|
||||
<h2 id="custom-hook-logic"><a class="header" href="#custom-hook-logic">Custom Hook Logic</a></h2>
|
||||
<p>You can use <a href="https://docs.rs/dioxus/latest/dioxus/prelude/struct.ScopeState.html#method.use_hook"><code>cx.use_hook</code></a> to build your own hooks. In fact, this is what all the standard hooks are built on!</p>
|
||||
<p><code>use_hook</code> accepts a single closure for initializing the hook. It will be only run the first time the component is rendered. The return value of that closure will be used as the value of the hook – Dioxus will take it, and store it for as long as the component is alive. On every render (not just the first one!), you will get a reference to this value.</p>
|
||||
<blockquote>
|
||||
<p>Note: You can implement <a href="https://doc.rust-lang.org/std/ops/trait.Drop.html"><code>Drop</code></a> for your hook value – it will be dropped then the component is unmounted (no longer in the UI)</p>
|
||||
</blockquote>
|
||||
<p>Inside the initialization closure, you will typically make calls to other <code>cx</code> methods. For example:</p>
|
||||
<ul>
|
||||
<li>The <code>use_state</code> hook tracks state in the hook value, and uses <a href="https://docs.rs/dioxus/latest/dioxus/prelude/struct.ScopeState.html#method.schedule_update"><code>cx.schedule_update</code></a> to make Dioxus re-render the component whenever it changes.</li>
|
||||
</ul>
|
||||
<p>Here is a simplified implementation of the <code>use_state</code> hook:</p>
|
||||
<pre><pre class="playground"><code class="language-rust edition2018">
|
||||
<span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Clone)]
|
||||
struct UseState<T> {
|
||||
value: Rc<RefCell<T>>,
|
||||
update: Arc<dyn Fn()>,
|
||||
}
|
||||
|
||||
fn my_use_state<T: 'static>(cx: &ScopeState, init: impl FnOnce() -> T) -> &UseState<T> {
|
||||
cx.use_hook(|| {
|
||||
// The update function will trigger a re-render in the component cx is attached to
|
||||
let update = cx.schedule_update();
|
||||
// Create the initial state
|
||||
let value = Rc::new(RefCell::new(init()));
|
||||
|
||||
UseState { value, update }
|
||||
})
|
||||
}
|
||||
|
||||
impl<T: Clone> UseState<T> {
|
||||
fn get(&self) -> T {
|
||||
self.value.borrow().clone()
|
||||
}
|
||||
|
||||
fn set(&self, value: T) {
|
||||
// Update the state
|
||||
*self.value.borrow_mut() = value;
|
||||
// Trigger a re-render on the component the state is from
|
||||
(self.update)();
|
||||
}
|
||||
}
|
||||
<span class="boring">}
|
||||
</span></code></pre></pre>
|
||||
<ul>
|
||||
<li>The <code>use_context</code> hook calls <a href="https://docs.rs/dioxus/latest/dioxus/prelude/struct.ScopeState.html#method.consume_context"><code>cx.consume_context</code></a> (which would be expensive to call on every render) to get some context from the scope</li>
|
||||
</ul>
|
||||
<p>Here is an implementation of the <code>use_context</code> and <code>use_context_provider</code> hooks:</p>
|
||||
<pre><pre class="playground"><code class="language-rust edition2018">
|
||||
<span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>pub fn use_context<T: 'static + Clone>(cx: &ScopeState) -> Option<&T> {
|
||||
cx.use_hook(|| cx.consume_context::<T>()).as_ref()
|
||||
}
|
||||
|
||||
pub fn use_context_provider<T: 'static + Clone>(cx: &ScopeState, f: impl FnOnce() -> T) -> &T {
|
||||
cx.use_hook(|| {
|
||||
let val = f();
|
||||
// Provide the context state to the scope
|
||||
cx.provide_context(val.clone());
|
||||
val
|
||||
})
|
||||
}
|
||||
|
||||
<span class="boring">}
|
||||
</span></code></pre></pre>
|
||||
<h2 id="hook-anti-patterns"><a class="header" href="#hook-anti-patterns">Hook Anti-Patterns</a></h2>
|
||||
<p>When writing a custom hook, you should avoid the following anti-patterns:</p>
|
||||
<ul>
|
||||
<li>!Clone Hooks: To allow hooks to be used within async blocks, the hooks must be Clone. To make a hook clone, you can wrap data in Rc or Arc and avoid lifetimes in hooks.</li>
|
||||
</ul>
|
||||
<p>This version of use_state may seem more efficient, but it is not cloneable:</p>
|
||||
<pre><pre class="playground"><code class="language-rust edition2018">
|
||||
<span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
struct UseState<'a, T> {
|
||||
value: &'a RefCell<T>,
|
||||
update: Arc<dyn Fn()>,
|
||||
}
|
||||
|
||||
fn my_use_state<T: 'static>(cx: &ScopeState, init: impl FnOnce() -> T) -> UseState<T> {
|
||||
// The update function will trigger a re-render in the component cx is attached to
|
||||
let update = cx.schedule_update();
|
||||
// Create the initial state
|
||||
let value = cx.use_hook(|| RefCell::new(init()));
|
||||
|
||||
UseState { value, update }
|
||||
}
|
||||
|
||||
impl<T: Clone> UseState<'_, T> {
|
||||
fn get(&self) -> T {
|
||||
self.value.borrow().clone()
|
||||
}
|
||||
|
||||
fn set(&self, value: T) {
|
||||
// Update the state
|
||||
*self.value.borrow_mut() = value;
|
||||
// Trigger a re-render on the component the state is from
|
||||
(self.update)();
|
||||
}
|
||||
}
|
||||
<span class="boring">}
|
||||
</span></code></pre></pre>
|
||||
<p>If we try to use this hook in an async block, we will get a compile error:</p>
|
||||
<pre><pre class="playground"><code class="language-rust edition2018">
|
||||
<span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>fn FutureComponent(cx: &ScopeState) -> Element {
|
||||
let my_state = my_use_state(cx, || 0);
|
||||
cx.spawn({
|
||||
to_owned![my_state];
|
||||
async move {
|
||||
my_state.set(1);
|
||||
}
|
||||
});
|
||||
|
||||
todo!()
|
||||
}
|
||||
<span class="boring">}
|
||||
</span></code></pre></pre>
|
||||
<p>But with the original version, we can use it in an async block:</p>
|
||||
<pre><pre class="playground"><code class="language-rust edition2018">
|
||||
<span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>fn FutureComponent(cx: &ScopeState) -> Element {
|
||||
let my_state = use_state(cx, || 0);
|
||||
cx.spawn({
|
||||
to_owned![my_state];
|
||||
async move {
|
||||
my_state.set(1);
|
||||
}
|
||||
});
|
||||
|
||||
todo!()
|
||||
}
|
||||
<span class="boring">}
|
||||
</span></code></pre></pre>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../interactivity/sharing_state.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next" href="../interactivity/dynamic_rendering.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../interactivity/sharing_state.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next" href="../interactivity/dynamic_rendering.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
window.playground_line_numbers = true;
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
window.playground_copyable = true;
|
||||
</script>
|
||||
|
||||
<script src="../ace.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../editor.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../mode-rust.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../theme-dawn.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../theme-tomorrow_night.js" type="text/javascript" charset="utf-8"></script>
|
||||
|
||||
<script src="../elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../mark.min.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../searcher.js" type="text/javascript" charset="utf-8"></script>
|
||||
|
||||
<script src="../clipboard.min.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../highlight.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../book.js" type="text/javascript" charset="utf-8"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,418 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="sidebar-visible no-js light">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>Dynamic Rendering</title>
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff" />
|
||||
|
||||
<link rel="icon" href="../favicon.svg">
|
||||
<link rel="shortcut icon" href="../favicon.png">
|
||||
<link rel="stylesheet" href="../css/variables.css">
|
||||
<link rel="stylesheet" href="../css/general.css">
|
||||
<link rel="stylesheet" href="../css/chrome.css">
|
||||
<link rel="stylesheet" href="../css/print.css" media="print">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
|
||||
<link rel="stylesheet" href="../fonts/fonts.css">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" href="../highlight.css">
|
||||
<link rel="stylesheet" href="../tomorrow-night.css">
|
||||
<link rel="stylesheet" href="../ayu-highlight.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
|
||||
<!-- MathJax -->
|
||||
<script async type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Provide site root to javascript -->
|
||||
<script type="text/javascript">
|
||||
var path_to_root = "../";
|
||||
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "navy" : "light";
|
||||
</script>
|
||||
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script type="text/javascript">
|
||||
try {
|
||||
var theme = localStorage.getItem('mdbook-theme');
|
||||
var sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script type="text/javascript">
|
||||
var theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
var html = document.querySelector('html');
|
||||
html.classList.remove('no-js')
|
||||
html.classList.remove('light')
|
||||
html.classList.add(theme);
|
||||
html.classList.add('js');
|
||||
</script>
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script type="text/javascript">
|
||||
var html = document.querySelector('html');
|
||||
var sidebar = 'hidden';
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
}
|
||||
html.classList.remove('sidebar-visible');
|
||||
html.classList.add("sidebar-" + sidebar);
|
||||
</script>
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<div class="sidebar-scrollbox">
|
||||
<ol class="chapter"><li class="chapter-item expanded affix "><a href="../index.html">Introduction</a></li><li class="chapter-item expanded "><a href="../getting_started/index.html"><strong aria-hidden="true">1.</strong> Getting Started</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../getting_started/desktop.html"><strong aria-hidden="true">1.1.</strong> Desktop</a></li><li class="chapter-item expanded "><a href="../getting_started/web.html"><strong aria-hidden="true">1.2.</strong> Web</a></li><li class="chapter-item expanded "><a href="../getting_started/ssr.html"><strong aria-hidden="true">1.3.</strong> Server-Side Rendering</a></li><li class="chapter-item expanded "><a href="../getting_started/fullstack.html"><strong aria-hidden="true">1.4.</strong> Fullstack</a></li><li class="chapter-item expanded "><a href="../getting_started/liveview.html"><strong aria-hidden="true">1.5.</strong> Liveview</a></li><li class="chapter-item expanded "><a href="../getting_started/tui.html"><strong aria-hidden="true">1.6.</strong> Terminal UI</a></li><li class="chapter-item expanded "><a href="../getting_started/mobile.html"><strong aria-hidden="true">1.7.</strong> Mobile</a></li><li class="chapter-item expanded "><a href="../getting_started/hot_reload.html"><strong aria-hidden="true">1.8.</strong> Hot Reloading</a></li></ol></li><li class="chapter-item expanded "><a href="../describing_ui/index.html"><strong aria-hidden="true">2.</strong> Describing the UI</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../describing_ui/special_attributes.html"><strong aria-hidden="true">2.1.</strong> Special Attributes</a></li><li class="chapter-item expanded "><a href="../describing_ui/components.html"><strong aria-hidden="true">2.2.</strong> Components</a></li><li class="chapter-item expanded "><a href="../describing_ui/component_props.html"><strong aria-hidden="true">2.3.</strong> Props</a></li><li class="chapter-item expanded "><a href="../describing_ui/component_children.html"><strong aria-hidden="true">2.4.</strong> Component Children</a></li></ol></li><li class="chapter-item expanded "><a href="../interactivity/index.html"><strong aria-hidden="true">3.</strong> Interactivity</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../interactivity/event_handlers.html"><strong aria-hidden="true">3.1.</strong> Event Listeners</a></li><li class="chapter-item expanded "><a href="../interactivity/hooks.html"><strong aria-hidden="true">3.2.</strong> Hooks & Component State</a></li><li class="chapter-item expanded "><a href="../interactivity/user_input.html"><strong aria-hidden="true">3.3.</strong> User Input</a></li><li class="chapter-item expanded "><a href="../interactivity/sharing_state.html"><strong aria-hidden="true">3.4.</strong> Sharing State</a></li><li class="chapter-item expanded "><a href="../interactivity/custom_hooks.html"><strong aria-hidden="true">3.5.</strong> Custom Hooks</a></li><li class="chapter-item expanded "><a href="../interactivity/dynamic_rendering.html" class="active"><strong aria-hidden="true">3.6.</strong> Dynamic Rendering</a></li><li class="chapter-item expanded "><a href="../interactivity/router.html"><strong aria-hidden="true">3.7.</strong> Routing</a></li></ol></li><li class="chapter-item expanded "><a href="../async/index.html"><strong aria-hidden="true">4.</strong> Async</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../async/use_future.html"><strong aria-hidden="true">4.1.</strong> UseFuture</a></li><li class="chapter-item expanded "><a href="../async/use_coroutine.html"><strong aria-hidden="true">4.2.</strong> UseCoroutine</a></li><li class="chapter-item expanded "><a href="../async/spawn.html"><strong aria-hidden="true">4.3.</strong> Spawning Futures</a></li></ol></li><li class="chapter-item expanded "><a href="../best_practices/index.html"><strong aria-hidden="true">5.</strong> Best Practices</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../best_practices/error_handling.html"><strong aria-hidden="true">5.1.</strong> Error Handling</a></li><li class="chapter-item expanded "><a href="../best_practices/antipatterns.html"><strong aria-hidden="true">5.2.</strong> Antipatterns</a></li></ol></li><li class="chapter-item expanded "><a href="../publishing/index.html"><strong aria-hidden="true">6.</strong> Publishing</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../publishing/desktop.html"><strong aria-hidden="true">6.1.</strong> Desktop</a></li><li class="chapter-item expanded "><a href="../publishing/web.html"><strong aria-hidden="true">6.2.</strong> Web</a></li><li class="spacer"></li></ol></li><li class="chapter-item expanded "><a href="../fullstack/index.html"><strong aria-hidden="true">7.</strong> Fullstack</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../fullstack/getting_started.html"><stLine truncated
|
||||
</div>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
<div id="menu-bar-hover-placeholder"></div>
|
||||
<div id="menu-bar" class="menu-bar sticky bordered">
|
||||
<div class="left-buttons">
|
||||
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<i class="fa fa-bars"></i>
|
||||
</button>
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<i class="fa fa-paint-brush"></i>
|
||||
</button>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="light">Light (default)</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||
</ul>
|
||||
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
<button id="language-toggle" class="icon-button" type="button" title="Select language" aria-label="Select language" aria-haspopup="true" aria-expanded="false" aria-controls="language-list">
|
||||
<i class="fa fa-globe"></i>
|
||||
</button>
|
||||
<ul id="language-list" class="language-popup" aria-label="Languages" role="menu">
|
||||
<li role="none"><a href="../../en/interactivity/dynamic_rendering.html"><button role="menuitem" class="language" id="light">English</button></a></li>
|
||||
<li role="none"><a href="../../pt-br/interactivity/dynamic_rendering.html"><button role="menuitem" class="language" id="light">Português Brasileiro</button></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title"></h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
||||
<i id="print-button" class="fa fa-print"></i>
|
||||
</a>
|
||||
<a href="https://github.com/DioxusLabs/dioxus/edit/master/docs/guide" title="Git repository" aria-label="Git repository">
|
||||
<i id="git-repository-button" class="fa fa-github"></i>
|
||||
</a>
|
||||
<a href="https://github.com/DioxusLabs/dioxus/edit/master/docs/guide/src/interactivity/dynamic_rendering.md" title="Suggest an edit" aria-label="Suggest an edit">
|
||||
<i id="git-edit-button" class="fa fa-edit"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="search-wrapper" class="hidden">
|
||||
<form id="searchbar-outer" class="searchbar-outer">
|
||||
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||
</form>
|
||||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script type="text/javascript">
|
||||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1 id="dynamic-rendering"><a class="header" href="#dynamic-rendering">Dynamic Rendering</a></h1>
|
||||
<p>Sometimes you want to render different things depending on the state/props. With Dioxus, just describe what you want to see using Rust control flow – the framework will take care of making the necessary changes on the fly if the state or props change!</p>
|
||||
<h2 id="conditional-rendering"><a class="header" href="#conditional-rendering">Conditional Rendering</a></h2>
|
||||
<p>To render different elements based on a condition, you could use an <code>if-else</code> statement:</p>
|
||||
<pre><pre class="playground"><code class="language-rust edition2018">
|
||||
<span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>if *is_logged_in {
|
||||
cx.render(rsx! {
|
||||
"Welcome!"
|
||||
button {
|
||||
onclick: move |_| on_log_out.call(()),
|
||||
"Log Out",
|
||||
}
|
||||
})
|
||||
} else {
|
||||
cx.render(rsx! {
|
||||
button {
|
||||
onclick: move |_| on_log_in.call(()),
|
||||
"Log In",
|
||||
}
|
||||
})
|
||||
}
|
||||
<span class="boring">}
|
||||
</span></code></pre></pre>
|
||||
<blockquote>
|
||||
<p>You could also use <code>match</code> statements, or any Rust function to conditionally render different things.</p>
|
||||
</blockquote>
|
||||
<h3 id="improving-the-if-else-example"><a class="header" href="#improving-the-if-else-example">Improving the <code>if-else</code> Example</a></h3>
|
||||
<p>You may have noticed some repeated code in the <code>if-else</code> example above. Repeating code like this is both bad for maintainability and performance. Dioxus will skip diffing static elements like the button, but when switching between multiple <code>rsx</code> calls it cannot perform this optimization. For this example either approach is fine, but for components with large parts that are reused between conditionals, it can be more of an issue.</p>
|
||||
<p>We can improve this example by splitting up the dynamic parts and inserting them where they are needed.</p>
|
||||
<pre><pre class="playground"><code class="language-rust edition2018">
|
||||
<span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>cx.render(rsx! {
|
||||
// We only render the welcome message if we are logged in
|
||||
// You can use if statements in the middle of a render block to conditionally render elements
|
||||
if *is_logged_in {
|
||||
// Notice the body of this if statment is rsx code, not an expression
|
||||
"Welcome!"
|
||||
}
|
||||
button {
|
||||
// depending on the value of `is_logged_in`, we will call a different event handler
|
||||
onclick: move |_| if *is_logged_in {
|
||||
on_log_in.call(())
|
||||
}
|
||||
else{
|
||||
on_log_out.call(())
|
||||
},
|
||||
if *is_logged_in {
|
||||
// if we are logged in, the button should say "Log Out"
|
||||
"Log Out"
|
||||
} else {
|
||||
// if we are not logged in, the button should say "Log In"
|
||||
"Log In"
|
||||
}
|
||||
}
|
||||
})
|
||||
<span class="boring">}
|
||||
</span></code></pre></pre>
|
||||
<h3 id="inspecting-element-props"><a class="header" href="#inspecting-element-props">Inspecting <code>Element</code> props</a></h3>
|
||||
<p>Since <code>Element</code> is a <code>Option<VNode></code>, components accepting <code>Element</code> as a prop can inspect its contents, and render different things based on that. Example:</p>
|
||||
<pre><pre class="playground"><code class="language-rust edition2018">
|
||||
<span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>fn Clickable<'a>(cx: Scope<'a, ClickableProps<'a>>) -> Element {
|
||||
match cx.props.children {
|
||||
Some(VNode { dynamic_nodes, .. }) => {
|
||||
todo!("render some stuff")
|
||||
}
|
||||
_ => {
|
||||
todo!("render some other stuff")
|
||||
}
|
||||
}
|
||||
}
|
||||
<span class="boring">}
|
||||
</span></code></pre></pre>
|
||||
<p>You can't mutate the <code>Element</code>, but if you need a modified version of it, you can construct a new one based on its attributes/children/etc.</p>
|
||||
<h2 id="rendering-nothing"><a class="header" href="#rendering-nothing">Rendering Nothing</a></h2>
|
||||
<p>To render nothing, you can return <code>None</code> from a component. This is useful if you want to conditionally hide something:</p>
|
||||
<pre><pre class="playground"><code class="language-rust edition2018">
|
||||
<span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>if *is_logged_in {
|
||||
return None;
|
||||
}
|
||||
|
||||
cx.render(rsx! {
|
||||
a {
|
||||
"You must be logged in to comment"
|
||||
}
|
||||
})
|
||||
<span class="boring">}
|
||||
</span></code></pre></pre>
|
||||
<p>This works because the <code>Element</code> type is just an alias for <code>Option<VNode></code></p>
|
||||
<blockquote>
|
||||
<p>Again, you may use a different method to conditionally return <code>None</code>. For example the boolean's <a href="https://doc.rust-lang.org/std/primitive.bool.html#method.then"><code>then()</code></a> function could be used.</p>
|
||||
</blockquote>
|
||||
<h2 id="rendering-lists"><a class="header" href="#rendering-lists">Rendering Lists</a></h2>
|
||||
<p>Often, you'll want to render a collection of components. For example, you might want to render a list of all comments on a post.</p>
|
||||
<p>For this, Dioxus accepts iterators that produce <code>Element</code>s. So we need to:</p>
|
||||
<ul>
|
||||
<li>Get an iterator over all of our items (e.g., if you have a <code>Vec</code> of comments, iterate over it with <code>iter()</code>)</li>
|
||||
<li><code>.map</code> the iterator to convert each item into a <code>LazyNode</code> using <code>rsx!(...)</code>
|
||||
<ul>
|
||||
<li>Add a unique <code>key</code> attribute to each iterator item</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Include this iterator in the final RSX (or use it inline)</li>
|
||||
</ul>
|
||||
<p>Example: suppose you have a list of comments you want to render. Then, you can render them like this:</p>
|
||||
<pre><pre class="playground"><code class="language-rust edition2018">
|
||||
<span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>let comment_field = use_state(cx, String::new);
|
||||
let mut next_id = use_state(cx, || 0);
|
||||
let comments = use_ref(cx, Vec::<Comment>::new);
|
||||
|
||||
let comments_lock = comments.read();
|
||||
let comments_rendered = comments_lock.iter().map(|comment| {
|
||||
rsx!(CommentComponent {
|
||||
key: "{comment.id}",
|
||||
comment: comment.clone(),
|
||||
})
|
||||
});
|
||||
|
||||
cx.render(rsx!(
|
||||
form {
|
||||
onsubmit: move |_| {
|
||||
comments.write().push(Comment {
|
||||
content: comment_field.get().clone(),
|
||||
id: *next_id.get(),
|
||||
});
|
||||
next_id += 1;
|
||||
|
||||
comment_field.set(String::new());
|
||||
},
|
||||
input {
|
||||
value: "{comment_field}",
|
||||
oninput: |event| comment_field.set(event.value.clone()),
|
||||
}
|
||||
input {
|
||||
r#type: "submit",
|
||||
}
|
||||
},
|
||||
comments_rendered,
|
||||
))
|
||||
<span class="boring">}
|
||||
</span></code></pre></pre>
|
||||
<h3 id="inline-for-loops"><a class="header" href="#inline-for-loops">Inline for loops</a></h3>
|
||||
<p>Because of how common it is to render a list of items, Dioxus provides a shorthand for this. Instead of using <code>.iter, </code>.map<code>, and </code>rsx<code>, you can use a </code>for` loop with a body of rsx code:</p>
|
||||
<pre><pre class="playground"><code class="language-rust edition2018">
|
||||
<span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>let comment_field = use_state(cx, String::new);
|
||||
let mut next_id = use_state(cx, || 0);
|
||||
let comments = use_ref(cx, Vec::<Comment>::new);
|
||||
|
||||
cx.render(rsx!(
|
||||
form {
|
||||
onsubmit: move |_| {
|
||||
comments.write().push(Comment {
|
||||
content: comment_field.get().clone(),
|
||||
id: *next_id.get(),
|
||||
});
|
||||
next_id += 1;
|
||||
|
||||
comment_field.set(String::new());
|
||||
},
|
||||
input {
|
||||
value: "{comment_field}",
|
||||
oninput: |event| comment_field.set(event.value.clone()),
|
||||
}
|
||||
input {
|
||||
r#type: "submit",
|
||||
}
|
||||
},
|
||||
for comment in &*comments.read() {
|
||||
// Notice the body of this for loop is rsx code, not an expression
|
||||
CommentComponent {
|
||||
key: "{comment.id}",
|
||||
comment: comment.clone(),
|
||||
}
|
||||
}
|
||||
))
|
||||
<span class="boring">}
|
||||
</span></code></pre></pre>
|
||||
<h3 id="the-key-attribute"><a class="header" href="#the-key-attribute">The <code>key</code> Attribute</a></h3>
|
||||
<p>Every time you re-render your list, Dioxus needs to keep track of which items go where to determine what updates need to be made to the UI.</p>
|
||||
<p>For example, suppose the <code>CommentComponent</code> had some state – e.g. a field where the user typed in a reply. If the order of comments suddenly changes, Dioxus needs to correctly associate that state with the same comment – otherwise, the user will end up replying to a different comment!</p>
|
||||
<p>To help Dioxus keep track of list items, we need to associate each item with a unique key. In the example above, we dynamically generated the unique key. In real applications, it's more likely that the key will come from e.g. a database ID. It doesn't matter where you get the key from, as long as it meets the requirements:</p>
|
||||
<ul>
|
||||
<li>Keys must be unique in a list</li>
|
||||
<li>The same item should always get associated with the same key</li>
|
||||
<li>Keys should be relatively small (i.e. converting the entire Comment structure to a String would be a pretty bad key) so they can be compared efficiently</li>
|
||||
</ul>
|
||||
<p>You might be tempted to use an item's index in the list as its key. That’s what Dioxus will use if you don’t specify a key at all. This is only acceptable if you can guarantee that the list is constant – i.e., no re-ordering, additions, or deletions.</p>
|
||||
<blockquote>
|
||||
<p>Note that if you pass the key to a component you've made, it won't receive the key as a prop. It’s only used as a hint by Dioxus itself. If your component needs an ID, you have to pass it as a separate prop.</p>
|
||||
</blockquote>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../interactivity/custom_hooks.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next" href="../interactivity/router.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../interactivity/custom_hooks.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next" href="../interactivity/router.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
window.playground_line_numbers = true;
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
window.playground_copyable = true;
|
||||
</script>
|
||||
|
||||
<script src="../ace.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../editor.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../mode-rust.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../theme-dawn.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../theme-tomorrow_night.js" type="text/javascript" charset="utf-8"></script>
|
||||
|
||||
<script src="../elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../mark.min.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../searcher.js" type="text/javascript" charset="utf-8"></script>
|
||||
|
||||
<script src="../clipboard.min.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../highlight.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../book.js" type="text/javascript" charset="utf-8"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,316 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="sidebar-visible no-js light">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>Event Listeners</title>
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff" />
|
||||
|
||||
<link rel="icon" href="../favicon.svg">
|
||||
<link rel="shortcut icon" href="../favicon.png">
|
||||
<link rel="stylesheet" href="../css/variables.css">
|
||||
<link rel="stylesheet" href="../css/general.css">
|
||||
<link rel="stylesheet" href="../css/chrome.css">
|
||||
<link rel="stylesheet" href="../css/print.css" media="print">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
|
||||
<link rel="stylesheet" href="../fonts/fonts.css">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" href="../highlight.css">
|
||||
<link rel="stylesheet" href="../tomorrow-night.css">
|
||||
<link rel="stylesheet" href="../ayu-highlight.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
|
||||
<!-- MathJax -->
|
||||
<script async type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Provide site root to javascript -->
|
||||
<script type="text/javascript">
|
||||
var path_to_root = "../";
|
||||
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "navy" : "light";
|
||||
</script>
|
||||
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script type="text/javascript">
|
||||
try {
|
||||
var theme = localStorage.getItem('mdbook-theme');
|
||||
var sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script type="text/javascript">
|
||||
var theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
var html = document.querySelector('html');
|
||||
html.classList.remove('no-js')
|
||||
html.classList.remove('light')
|
||||
html.classList.add(theme);
|
||||
html.classList.add('js');
|
||||
</script>
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script type="text/javascript">
|
||||
var html = document.querySelector('html');
|
||||
var sidebar = 'hidden';
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
}
|
||||
html.classList.remove('sidebar-visible');
|
||||
html.classList.add("sidebar-" + sidebar);
|
||||
</script>
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<div class="sidebar-scrollbox">
|
||||
<ol class="chapter"><li class="chapter-item expanded affix "><a href="../index.html">Introduction</a></li><li class="chapter-item expanded "><a href="../getting_started/index.html"><strong aria-hidden="true">1.</strong> Getting Started</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../getting_started/desktop.html"><strong aria-hidden="true">1.1.</strong> Desktop</a></li><li class="chapter-item expanded "><a href="../getting_started/web.html"><strong aria-hidden="true">1.2.</strong> Web</a></li><li class="chapter-item expanded "><a href="../getting_started/ssr.html"><strong aria-hidden="true">1.3.</strong> Server-Side Rendering</a></li><li class="chapter-item expanded "><a href="../getting_started/fullstack.html"><strong aria-hidden="true">1.4.</strong> Fullstack</a></li><li class="chapter-item expanded "><a href="../getting_started/liveview.html"><strong aria-hidden="true">1.5.</strong> Liveview</a></li><li class="chapter-item expanded "><a href="../getting_started/tui.html"><strong aria-hidden="true">1.6.</strong> Terminal UI</a></li><li class="chapter-item expanded "><a href="../getting_started/mobile.html"><strong aria-hidden="true">1.7.</strong> Mobile</a></li><li class="chapter-item expanded "><a href="../getting_started/hot_reload.html"><strong aria-hidden="true">1.8.</strong> Hot Reloading</a></li></ol></li><li class="chapter-item expanded "><a href="../describing_ui/index.html"><strong aria-hidden="true">2.</strong> Describing the UI</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../describing_ui/special_attributes.html"><strong aria-hidden="true">2.1.</strong> Special Attributes</a></li><li class="chapter-item expanded "><a href="../describing_ui/components.html"><strong aria-hidden="true">2.2.</strong> Components</a></li><li class="chapter-item expanded "><a href="../describing_ui/component_props.html"><strong aria-hidden="true">2.3.</strong> Props</a></li><li class="chapter-item expanded "><a href="../describing_ui/component_children.html"><strong aria-hidden="true">2.4.</strong> Component Children</a></li></ol></li><li class="chapter-item expanded "><a href="../interactivity/index.html"><strong aria-hidden="true">3.</strong> Interactivity</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../interactivity/event_handlers.html" class="active"><strong aria-hidden="true">3.1.</strong> Event Listeners</a></li><li class="chapter-item expanded "><a href="../interactivity/hooks.html"><strong aria-hidden="true">3.2.</strong> Hooks & Component State</a></li><li class="chapter-item expanded "><a href="../interactivity/user_input.html"><strong aria-hidden="true">3.3.</strong> User Input</a></li><li class="chapter-item expanded "><a href="../interactivity/sharing_state.html"><strong aria-hidden="true">3.4.</strong> Sharing State</a></li><li class="chapter-item expanded "><a href="../interactivity/custom_hooks.html"><strong aria-hidden="true">3.5.</strong> Custom Hooks</a></li><li class="chapter-item expanded "><a href="../interactivity/dynamic_rendering.html"><strong aria-hidden="true">3.6.</strong> Dynamic Rendering</a></li><li class="chapter-item expanded "><a href="../interactivity/router.html"><strong aria-hidden="true">3.7.</strong> Routing</a></li></ol></li><li class="chapter-item expanded "><a href="../async/index.html"><strong aria-hidden="true">4.</strong> Async</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../async/use_future.html"><strong aria-hidden="true">4.1.</strong> UseFuture</a></li><li class="chapter-item expanded "><a href="../async/use_coroutine.html"><strong aria-hidden="true">4.2.</strong> UseCoroutine</a></li><li class="chapter-item expanded "><a href="../async/spawn.html"><strong aria-hidden="true">4.3.</strong> Spawning Futures</a></li></ol></li><li class="chapter-item expanded "><a href="../best_practices/index.html"><strong aria-hidden="true">5.</strong> Best Practices</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../best_practices/error_handling.html"><strong aria-hidden="true">5.1.</strong> Error Handling</a></li><li class="chapter-item expanded "><a href="../best_practices/antipatterns.html"><strong aria-hidden="true">5.2.</strong> Antipatterns</a></li></ol></li><li class="chapter-item expanded "><a href="../publishing/index.html"><strong aria-hidden="true">6.</strong> Publishing</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../publishing/desktop.html"><strong aria-hidden="true">6.1.</strong> Desktop</a></li><li class="chapter-item expanded "><a href="../publishing/web.html"><strong aria-hidden="true">6.2.</strong> Web</a></li><li class="spacer"></li></ol></li><li class="chapter-item expanded "><a href="../fullstack/index.html"><strong aria-hidden="true">7.</strong> Fullstack</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../fullstack/getting_started.html"><stLine truncated
|
||||
</div>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
<div id="menu-bar-hover-placeholder"></div>
|
||||
<div id="menu-bar" class="menu-bar sticky bordered">
|
||||
<div class="left-buttons">
|
||||
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<i class="fa fa-bars"></i>
|
||||
</button>
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<i class="fa fa-paint-brush"></i>
|
||||
</button>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="light">Light (default)</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||
</ul>
|
||||
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
<button id="language-toggle" class="icon-button" type="button" title="Select language" aria-label="Select language" aria-haspopup="true" aria-expanded="false" aria-controls="language-list">
|
||||
<i class="fa fa-globe"></i>
|
||||
</button>
|
||||
<ul id="language-list" class="language-popup" aria-label="Languages" role="menu">
|
||||
<li role="none"><a href="../../en/interactivity/event_handlers.html"><button role="menuitem" class="language" id="light">English</button></a></li>
|
||||
<li role="none"><a href="../../pt-br/interactivity/event_handlers.html"><button role="menuitem" class="language" id="light">Português Brasileiro</button></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title"></h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
||||
<i id="print-button" class="fa fa-print"></i>
|
||||
</a>
|
||||
<a href="https://github.com/DioxusLabs/dioxus/edit/master/docs/guide" title="Git repository" aria-label="Git repository">
|
||||
<i id="git-repository-button" class="fa fa-github"></i>
|
||||
</a>
|
||||
<a href="https://github.com/DioxusLabs/dioxus/edit/master/docs/guide/src/interactivity/event_handlers.md" title="Suggest an edit" aria-label="Suggest an edit">
|
||||
<i id="git-edit-button" class="fa fa-edit"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="search-wrapper" class="hidden">
|
||||
<form id="searchbar-outer" class="searchbar-outer">
|
||||
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||
</form>
|
||||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script type="text/javascript">
|
||||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1 id="event-handlers"><a class="header" href="#event-handlers">Event Handlers</a></h1>
|
||||
<p>Event handlers are used to respond to user actions. For example, an event handler could be triggered when the user clicks, scrolls, moves the mouse, or types a character.</p>
|
||||
<p>Event handlers are attached to elements. For example, we usually don't care about all the clicks that happen within an app, only those on a particular button.</p>
|
||||
<p>Event handlers are similar to regular attributes, but their name usually starts with <code>on</code>- and they accept closures as values. The closure will be called whenever the event it listens for is triggered and will be passed that event.</p>
|
||||
<p>For example, to handle clicks on an element, we can specify an <code>onclick</code> handler:</p>
|
||||
<pre><pre class="playground"><code class="language-rust edition2018">
|
||||
<span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>cx.render(rsx! {
|
||||
button {
|
||||
onclick: move |event| println!("Clicked! Event: {event:?}"),
|
||||
"click me!"
|
||||
}
|
||||
})
|
||||
<span class="boring">}
|
||||
</span></code></pre></pre>
|
||||
<h2 id="the-event-object"><a class="header" href="#the-event-object">The <code>Event</code> object</a></h2>
|
||||
<p>Event handlers receive an <a href="https://docs.rs/dioxus-core/latest/dioxus_core/struct.Event.html"><code>Event</code></a> object containing information about the event. Different types of events contain different types of data. For example, mouse-related events contain <a href="https://docs.rs/dioxus/latest/dioxus/events/struct.MouseData.html"><code>MouseData</code></a>, which tells you things like where the mouse was clicked and what mouse buttons were used.</p>
|
||||
<p>In the example above, this event data was logged to the terminal:</p>
|
||||
<pre><code>Clicked! Event: UiEvent { bubble_state: Cell { value: true }, data: MouseData { coordinates: Coordinates { screen: (242.0, 256.0), client: (26.0, 17.0), element: (16.0, 7.0), page: (26.0, 17.0) }, modifiers: (empty), held_buttons: EnumSet(), trigger_button: Some(Primary) } }
|
||||
Clicked! Event: UiEvent { bubble_state: Cell { value: true }, data: MouseData { coordinates: Coordinates { screen: (242.0, 256.0), client: (26.0, 17.0), element: (16.0, 7.0), page: (26.0, 17.0) }, modifiers: (empty), held_buttons: EnumSet(), trigger_button: Some(Primary) } }
|
||||
</code></pre>
|
||||
<p>To learn what the different event types for HTML provide, read the <a href="https://docs.rs/dioxus-html/latest/dioxus_html/events/index.html">events module docs</a>.</p>
|
||||
<h3 id="event-propagation"><a class="header" href="#event-propagation">Event propagation</a></h3>
|
||||
<p>Some events will trigger first on the element the event originated at upward. For example, a click event on a <code>button</code> inside a <code>div</code> would first trigger the button's event listener and then the div's event listener.</p>
|
||||
<blockquote>
|
||||
<p>For more information about event propigation see <a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#event_bubbling">the mdn docs on event bubling</a></p>
|
||||
</blockquote>
|
||||
<p>If you want to prevent this behavior, you can call <code>stop_propagation()</code> on the event:</p>
|
||||
<pre><pre class="playground"><code class="language-rust edition2018">
|
||||
<span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>cx.render(rsx! {
|
||||
div {
|
||||
onclick: move |_event| {},
|
||||
"outer",
|
||||
button {
|
||||
onclick: move |event| {
|
||||
// now, outer won't be triggered
|
||||
event.stop_propagation();
|
||||
},
|
||||
"inner"
|
||||
}
|
||||
}
|
||||
})
|
||||
<span class="boring">}
|
||||
</span></code></pre></pre>
|
||||
<h2 id="prevent-default"><a class="header" href="#prevent-default">Prevent Default</a></h2>
|
||||
<p>Some events have a default behavior. For keyboard events, this might be entering the typed character. For mouse events, this might be selecting some text.</p>
|
||||
<p>In some instances, might want to avoid this default behavior. For this, you can add the <code>prevent_default</code> attribute with the name of the handler whose default behavior you want to stop. This attribute can be used for multiple handlers using their name separated by spaces:</p>
|
||||
<pre><pre class="playground"><code class="language-rust edition2018">
|
||||
<span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>cx.render(rsx! {
|
||||
input {
|
||||
prevent_default: "oninput onclick",
|
||||
}
|
||||
})
|
||||
<span class="boring">}
|
||||
</span></code></pre></pre>
|
||||
<p>Any event handlers will still be called.</p>
|
||||
<blockquote>
|
||||
<p>Normally, in React or JavaScript, you'd call "preventDefault" on the event in the callback. Dioxus does <em>not</em> currently support this behavior. Note: this means you cannot conditionally prevent default behavior based on the data in the event.</p>
|
||||
</blockquote>
|
||||
<h2 id="handler-props"><a class="header" href="#handler-props">Handler Props</a></h2>
|
||||
<p>Sometimes, you might want to make a component that accepts an event handler. A simple example would be a <code>FancyButton</code> component, which accepts an <code>on_click</code> handler:</p>
|
||||
<pre><pre class="playground"><code class="language-rust edition2018">
|
||||
<span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>#[derive(Props)]
|
||||
pub struct FancyButtonProps<'a> {
|
||||
on_click: EventHandler<'a, MouseEvent>,
|
||||
}
|
||||
|
||||
pub fn FancyButton<'a>(cx: Scope<'a, FancyButtonProps<'a>>) -> Element<'a> {
|
||||
cx.render(rsx!(button {
|
||||
class: "fancy-button",
|
||||
onclick: move |evt| cx.props.on_click.call(evt),
|
||||
"click me pls."
|
||||
}))
|
||||
}
|
||||
<span class="boring">}
|
||||
</span></code></pre></pre>
|
||||
<p>Then, you can use it like any other handler:</p>
|
||||
<pre><pre class="playground"><code class="language-rust edition2018">
|
||||
<span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span> cx.render(rsx! {
|
||||
FancyButton {
|
||||
on_click: move |event| println!("Clicked! {event:?}")
|
||||
}
|
||||
})
|
||||
<span class="boring">}
|
||||
</span></code></pre></pre>
|
||||
<blockquote>
|
||||
<p>Note: just like any other attribute, you can name the handlers anything you want! Though they must start with <code>on</code>, for the prop to be automatically turned into an <code>EventHandler</code> at the call site.</p>
|
||||
<p>You can also put custom data in the event, rather than e.g. <code>MouseData</code></p>
|
||||
</blockquote>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../interactivity/index.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next" href="../interactivity/hooks.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../interactivity/index.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next" href="../interactivity/hooks.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
window.playground_line_numbers = true;
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
window.playground_copyable = true;
|
||||
</script>
|
||||
|
||||
<script src="../ace.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../editor.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../mode-rust.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../theme-dawn.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../theme-tomorrow_night.js" type="text/javascript" charset="utf-8"></script>
|
||||
|
||||
<script src="../elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../mark.min.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../searcher.js" type="text/javascript" charset="utf-8"></script>
|
||||
|
||||
<script src="../clipboard.min.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../highlight.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../book.js" type="text/javascript" charset="utf-8"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,388 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="sidebar-visible no-js light">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>Hooks & Component State</title>
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff" />
|
||||
|
||||
<link rel="icon" href="../favicon.svg">
|
||||
<link rel="shortcut icon" href="../favicon.png">
|
||||
<link rel="stylesheet" href="../css/variables.css">
|
||||
<link rel="stylesheet" href="../css/general.css">
|
||||
<link rel="stylesheet" href="../css/chrome.css">
|
||||
<link rel="stylesheet" href="../css/print.css" media="print">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
|
||||
<link rel="stylesheet" href="../fonts/fonts.css">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" href="../highlight.css">
|
||||
<link rel="stylesheet" href="../tomorrow-night.css">
|
||||
<link rel="stylesheet" href="../ayu-highlight.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
|
||||
<!-- MathJax -->
|
||||
<script async type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Provide site root to javascript -->
|
||||
<script type="text/javascript">
|
||||
var path_to_root = "../";
|
||||
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "navy" : "light";
|
||||
</script>
|
||||
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script type="text/javascript">
|
||||
try {
|
||||
var theme = localStorage.getItem('mdbook-theme');
|
||||
var sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script type="text/javascript">
|
||||
var theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
var html = document.querySelector('html');
|
||||
html.classList.remove('no-js')
|
||||
html.classList.remove('light')
|
||||
html.classList.add(theme);
|
||||
html.classList.add('js');
|
||||
</script>
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script type="text/javascript">
|
||||
var html = document.querySelector('html');
|
||||
var sidebar = 'hidden';
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
}
|
||||
html.classList.remove('sidebar-visible');
|
||||
html.classList.add("sidebar-" + sidebar);
|
||||
</script>
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<div class="sidebar-scrollbox">
|
||||
<ol class="chapter"><li class="chapter-item expanded affix "><a href="../index.html">Introduction</a></li><li class="chapter-item expanded "><a href="../getting_started/index.html"><strong aria-hidden="true">1.</strong> Getting Started</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../getting_started/desktop.html"><strong aria-hidden="true">1.1.</strong> Desktop</a></li><li class="chapter-item expanded "><a href="../getting_started/web.html"><strong aria-hidden="true">1.2.</strong> Web</a></li><li class="chapter-item expanded "><a href="../getting_started/ssr.html"><strong aria-hidden="true">1.3.</strong> Server-Side Rendering</a></li><li class="chapter-item expanded "><a href="../getting_started/fullstack.html"><strong aria-hidden="true">1.4.</strong> Fullstack</a></li><li class="chapter-item expanded "><a href="../getting_started/liveview.html"><strong aria-hidden="true">1.5.</strong> Liveview</a></li><li class="chapter-item expanded "><a href="../getting_started/tui.html"><strong aria-hidden="true">1.6.</strong> Terminal UI</a></li><li class="chapter-item expanded "><a href="../getting_started/mobile.html"><strong aria-hidden="true">1.7.</strong> Mobile</a></li><li class="chapter-item expanded "><a href="../getting_started/hot_reload.html"><strong aria-hidden="true">1.8.</strong> Hot Reloading</a></li></ol></li><li class="chapter-item expanded "><a href="../describing_ui/index.html"><strong aria-hidden="true">2.</strong> Describing the UI</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../describing_ui/special_attributes.html"><strong aria-hidden="true">2.1.</strong> Special Attributes</a></li><li class="chapter-item expanded "><a href="../describing_ui/components.html"><strong aria-hidden="true">2.2.</strong> Components</a></li><li class="chapter-item expanded "><a href="../describing_ui/component_props.html"><strong aria-hidden="true">2.3.</strong> Props</a></li><li class="chapter-item expanded "><a href="../describing_ui/component_children.html"><strong aria-hidden="true">2.4.</strong> Component Children</a></li></ol></li><li class="chapter-item expanded "><a href="../interactivity/index.html"><strong aria-hidden="true">3.</strong> Interactivity</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../interactivity/event_handlers.html"><strong aria-hidden="true">3.1.</strong> Event Listeners</a></li><li class="chapter-item expanded "><a href="../interactivity/hooks.html" class="active"><strong aria-hidden="true">3.2.</strong> Hooks & Component State</a></li><li class="chapter-item expanded "><a href="../interactivity/user_input.html"><strong aria-hidden="true">3.3.</strong> User Input</a></li><li class="chapter-item expanded "><a href="../interactivity/sharing_state.html"><strong aria-hidden="true">3.4.</strong> Sharing State</a></li><li class="chapter-item expanded "><a href="../interactivity/custom_hooks.html"><strong aria-hidden="true">3.5.</strong> Custom Hooks</a></li><li class="chapter-item expanded "><a href="../interactivity/dynamic_rendering.html"><strong aria-hidden="true">3.6.</strong> Dynamic Rendering</a></li><li class="chapter-item expanded "><a href="../interactivity/router.html"><strong aria-hidden="true">3.7.</strong> Routing</a></li></ol></li><li class="chapter-item expanded "><a href="../async/index.html"><strong aria-hidden="true">4.</strong> Async</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../async/use_future.html"><strong aria-hidden="true">4.1.</strong> UseFuture</a></li><li class="chapter-item expanded "><a href="../async/use_coroutine.html"><strong aria-hidden="true">4.2.</strong> UseCoroutine</a></li><li class="chapter-item expanded "><a href="../async/spawn.html"><strong aria-hidden="true">4.3.</strong> Spawning Futures</a></li></ol></li><li class="chapter-item expanded "><a href="../best_practices/index.html"><strong aria-hidden="true">5.</strong> Best Practices</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../best_practices/error_handling.html"><strong aria-hidden="true">5.1.</strong> Error Handling</a></li><li class="chapter-item expanded "><a href="../best_practices/antipatterns.html"><strong aria-hidden="true">5.2.</strong> Antipatterns</a></li></ol></li><li class="chapter-item expanded "><a href="../publishing/index.html"><strong aria-hidden="true">6.</strong> Publishing</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../publishing/desktop.html"><strong aria-hidden="true">6.1.</strong> Desktop</a></li><li class="chapter-item expanded "><a href="../publishing/web.html"><strong aria-hidden="true">6.2.</strong> Web</a></li><li class="spacer"></li></ol></li><li class="chapter-item expanded "><a href="../fullstack/index.html"><strong aria-hidden="true">7.</strong> Fullstack</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../fullstack/getting_started.html"><stLine truncated
|
||||
</div>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
<div id="menu-bar-hover-placeholder"></div>
|
||||
<div id="menu-bar" class="menu-bar sticky bordered">
|
||||
<div class="left-buttons">
|
||||
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<i class="fa fa-bars"></i>
|
||||
</button>
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<i class="fa fa-paint-brush"></i>
|
||||
</button>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="light">Light (default)</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||
</ul>
|
||||
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
<button id="language-toggle" class="icon-button" type="button" title="Select language" aria-label="Select language" aria-haspopup="true" aria-expanded="false" aria-controls="language-list">
|
||||
<i class="fa fa-globe"></i>
|
||||
</button>
|
||||
<ul id="language-list" class="language-popup" aria-label="Languages" role="menu">
|
||||
<li role="none"><a href="../../en/interactivity/hooks.html"><button role="menuitem" class="language" id="light">English</button></a></li>
|
||||
<li role="none"><a href="../../pt-br/interactivity/hooks.html"><button role="menuitem" class="language" id="light">Português Brasileiro</button></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title"></h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
||||
<i id="print-button" class="fa fa-print"></i>
|
||||
</a>
|
||||
<a href="https://github.com/DioxusLabs/dioxus/edit/master/docs/guide" title="Git repository" aria-label="Git repository">
|
||||
<i id="git-repository-button" class="fa fa-github"></i>
|
||||
</a>
|
||||
<a href="https://github.com/DioxusLabs/dioxus/edit/master/docs/guide/src/interactivity/hooks.md" title="Suggest an edit" aria-label="Suggest an edit">
|
||||
<i id="git-edit-button" class="fa fa-edit"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="search-wrapper" class="hidden">
|
||||
<form id="searchbar-outer" class="searchbar-outer">
|
||||
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||
</form>
|
||||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script type="text/javascript">
|
||||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1 id="hooks-and-component-state"><a class="header" href="#hooks-and-component-state">Hooks and Component State</a></h1>
|
||||
<p>So far our components have had no state like a normal rust functions. However, in a UI component, it is often useful to have stateful functionality to build user interactions. For example, you might want to track whether the user has opened a drop-down, and render different things accordingly.</p>
|
||||
<p>Hooks allow us to create state in our components. Hooks are Rust functions that take a reference to <code>ScopeState</code> (in a component, you can pass <code>cx</code>), and provide you with functionality and state.</p>
|
||||
<h2 id="use_state-hook"><a class="header" href="#use_state-hook"><code>use_state</code> Hook</a></h2>
|
||||
<p><a href="https://docs.rs/dioxus/latest/dioxus/prelude/fn.use_state.html"><code>use_state</code></a> is one of the simplest hooks.</p>
|
||||
<ul>
|
||||
<li>You provide a closure that determines the initial value</li>
|
||||
<li><code>use_state</code> gives you the current value, and a way to update it by setting it to something else</li>
|
||||
<li>When the value updates, <code>use_state</code> makes the component re-render, and provides you with the new value</li>
|
||||
</ul>
|
||||
<p>For example, you might have seen the counter example, in which state (a number) is tracked using the <code>use_state</code> hook:</p>
|
||||
<pre><pre class="playground"><code class="language-rust edition2018">
|
||||
<span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>fn App(cx: Scope) -> Element {
|
||||
// count will be initialized to 0 the first time the component is rendered
|
||||
let mut count = use_state(cx, || 0);
|
||||
|
||||
cx.render(rsx!(
|
||||
h1 { "High-Five counter: {count}" }
|
||||
button {
|
||||
onclick: move |_| {
|
||||
// changing the count will cause the component to re-render
|
||||
count += 1
|
||||
},
|
||||
"Up high!"
|
||||
}
|
||||
button {
|
||||
onclick: move |_| {
|
||||
// changing the count will cause the component to re-render
|
||||
count -= 1
|
||||
},
|
||||
"Down low!"
|
||||
}
|
||||
))
|
||||
}
|
||||
<span class="boring">}
|
||||
</span></code></pre></pre>
|
||||
<p><img src="./images/counter.png" alt="Screenshot: counter app" /></p>
|
||||
<p>Every time the component's state changes, it re-renders, and the component function is called, so you can describe what you want the new UI to look like. You don't have to worry about "changing" anything – just describe what you want in terms of the state, and Dioxus will take care of the rest!</p>
|
||||
<blockquote>
|
||||
<p><code>use_state</code> returns your value wrapped in a smart pointer of type <a href="https://docs.rs/dioxus/latest/dioxus/prelude/struct.UseState.html"><code>UseState</code></a>. This is why you can both read the value and update it, even within an event handler.</p>
|
||||
</blockquote>
|
||||
<p>You can use multiple hooks in the same component if you want:</p>
|
||||
<pre><pre class="playground"><code class="language-rust edition2018">
|
||||
<span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>fn App(cx: Scope) -> Element {
|
||||
let mut count_a = use_state(cx, || 0);
|
||||
let mut count_b = use_state(cx, || 0);
|
||||
|
||||
cx.render(rsx!(
|
||||
h1 { "Counter_a: {count_a}" }
|
||||
button { onclick: move |_| count_a += 1, "a++" }
|
||||
button { onclick: move |_| count_a -= 1, "a--" }
|
||||
h1 { "Counter_b: {count_b}" }
|
||||
button { onclick: move |_| count_b += 1, "b++" }
|
||||
button { onclick: move |_| count_b -= 1, "b--" }
|
||||
))
|
||||
}
|
||||
<span class="boring">}
|
||||
</span></code></pre></pre>
|
||||
<p><img src="./images/counter_two_state.png" alt="Screenshot: app with two counters" /></p>
|
||||
<h2 id="rules-of-hooks"><a class="header" href="#rules-of-hooks">Rules of Hooks</a></h2>
|
||||
<p>The above example might seem a bit magic, since Rust functions are typically not associated with state. Dioxus allows hooks to maintain state across renders through a reference to <code>ScopeState</code>, which is why you must pass <code>&cx</code> to them.</p>
|
||||
<p>But how can Dioxus differentiate between multiple hooks in the same component? As you saw in the second example, both <code>use_state</code> functions were called with the same parameters, so how come they can return different things when the counters are different?</p>
|
||||
<pre><pre class="playground"><code class="language-rust edition2018">
|
||||
<span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span> let mut count_a = use_state(cx, || 0);
|
||||
let mut count_b = use_state(cx, || 0);
|
||||
<span class="boring">}
|
||||
</span></code></pre></pre>
|
||||
<p>This is only possible because the two hooks are always called in the same order, so Dioxus knows which is which. Because the order you call hooks matters, you must follow certain rules when using hooks:</p>
|
||||
<ol>
|
||||
<li>Hooks may be only used in components or other hooks (we'll get to that later)</li>
|
||||
<li>On every call to the component function
|
||||
<ol>
|
||||
<li>The same hooks must be called (except in the case of early returns, as explained later in the <a href="../best_practices/error_handling.html">Error Handling chapter</a>)</li>
|
||||
<li>In the same order</li>
|
||||
</ol>
|
||||
</li>
|
||||
<li>Hooks name's should start with <code>use_</code> so you don't accidentally confuse them with regular functions</li>
|
||||
</ol>
|
||||
<p>These rules mean that there are certain things you can't do with hooks:</p>
|
||||
<h3 id="no-hooks-in-conditionals"><a class="header" href="#no-hooks-in-conditionals">No Hooks in Conditionals</a></h3>
|
||||
<pre><pre class="playground"><code class="language-rust edition2018">
|
||||
<span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>// ❌ don't call hooks in conditionals!
|
||||
// We must ensure that the same hooks will be called every time
|
||||
// But `if` statements only run if the conditional is true!
|
||||
// So we might violate rule 2.
|
||||
if you_are_happy && you_know_it {
|
||||
let something = use_state(cx, || "hands");
|
||||
println!("clap your {something}")
|
||||
}
|
||||
|
||||
// ✅ instead, *always* call use_state
|
||||
// You can put other stuff in the conditional though
|
||||
let something = use_state(cx, || "hands");
|
||||
if you_are_happy && you_know_it {
|
||||
println!("clap your {something}")
|
||||
}
|
||||
<span class="boring">}
|
||||
</span></code></pre></pre>
|
||||
<h3 id="no-hooks-in-closures"><a class="header" href="#no-hooks-in-closures">No Hooks in Closures</a></h3>
|
||||
<pre><pre class="playground"><code class="language-rust edition2018">
|
||||
<span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>// ❌ don't call hooks inside closures!
|
||||
// We can't guarantee that the closure, if used, will be called in the same order every time
|
||||
let _a = || {
|
||||
let b = use_state(cx, || 0);
|
||||
b.get()
|
||||
};
|
||||
|
||||
// ✅ instead, move hook `b` outside
|
||||
let b = use_state(cx, || 0);
|
||||
let _a = || b.get();
|
||||
<span class="boring">}
|
||||
</span></code></pre></pre>
|
||||
<h3 id="no-hooks-in-loops"><a class="header" href="#no-hooks-in-loops">No Hooks in Loops</a></h3>
|
||||
<pre><pre class="playground"><code class="language-rust edition2018">
|
||||
<span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>// `names` is a Vec<&str>
|
||||
|
||||
// ❌ Do not use hooks in loops!
|
||||
// In this case, if the length of the Vec changes, we break rule 2
|
||||
for _name in &names {
|
||||
let is_selected = use_state(cx, || false);
|
||||
println!("selected: {is_selected}");
|
||||
}
|
||||
|
||||
// ✅ Instead, use a hashmap with use_ref
|
||||
let selection_map = use_ref(cx, HashMap::<&str, bool>::new);
|
||||
|
||||
for name in &names {
|
||||
let is_selected = selection_map.read()[name];
|
||||
println!("selected: {is_selected}");
|
||||
}
|
||||
<span class="boring">}
|
||||
</span></code></pre></pre>
|
||||
<h2 id="use_ref-hook"><a class="header" href="#use_ref-hook"><code>use_ref</code> Hook</a></h2>
|
||||
<p><code>use_state</code> is great for tracking simple values. However, you may notice in the <a href="https://docs.rs/dioxus/latest/dioxus/hooks/struct.UseState.html"><code>UseState</code> API</a> that the only way to modify its value is to replace it with something else (e.g., by calling <code>set</code>, or through one of the <code>+=</code>, <code>-=</code> operators). This works well when it is cheap to construct a value (such as any primitive). But what if you want to maintain more complex data in the components state?</p>
|
||||
<p>For example, suppose we want to maintain a <code>Vec</code> of values. If we stored it with <code>use_state</code>, the only way to add a new value to the list would be to create a new <code>Vec</code> with the additional value, and put it in the state. This is expensive! We want to modify the existing <code>Vec</code> instead.</p>
|
||||
<p>Thankfully, there is another hook for that, <code>use_ref</code>! It is similar to <code>use_state</code>, but it lets you get a mutable reference to the contained data.</p>
|
||||
<p>Here's a simple example that keeps a list of events in a <code>use_ref</code>. We can acquire write access to the state with <code>.with_mut()</code>, and then just <code>.push</code> a new value to the state:</p>
|
||||
<pre><pre class="playground"><code class="language-rust edition2018">
|
||||
<span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>fn App(cx: Scope) -> Element {
|
||||
let list = use_ref(cx, Vec::new);
|
||||
|
||||
cx.render(rsx!(
|
||||
p { "Current list: {list.read():?}" }
|
||||
button {
|
||||
onclick: move |event| {
|
||||
list.with_mut(|list| list.push(event));
|
||||
},
|
||||
"Click me!"
|
||||
}
|
||||
))
|
||||
}
|
||||
<span class="boring">}
|
||||
</span></code></pre></pre>
|
||||
<blockquote>
|
||||
<p>The return values of <code>use_state</code> and <code>use_ref</code> (<code>UseState</code> and <code>UseRef</code>, respectively) are in some ways similar to <a href="https://doc.rust-lang.org/std/cell/"><code>Cell</code></a> and <a href="https://doc.rust-lang.org/std/cell/struct.RefCell.html"><code>RefCell</code></a> – they provide interior mutability. However, these Dioxus wrappers also ensure that the component gets re-rendered whenever you change the state.</p>
|
||||
</blockquote>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../interactivity/event_handlers.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next" href="../interactivity/user_input.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../interactivity/event_handlers.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next" href="../interactivity/user_input.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
window.playground_line_numbers = true;
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
window.playground_copyable = true;
|
||||
</script>
|
||||
|
||||
<script src="../ace.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../editor.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../mode-rust.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../theme-dawn.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../theme-tomorrow_night.js" type="text/javascript" charset="utf-8"></script>
|
||||
|
||||
<script src="../elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../mark.min.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../searcher.js" type="text/javascript" charset="utf-8"></script>
|
||||
|
||||
<script src="../clipboard.min.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../highlight.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../book.js" type="text/javascript" charset="utf-8"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 362 KiB |
@@ -0,0 +1,220 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="sidebar-visible no-js light">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>Interactivity</title>
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff" />
|
||||
|
||||
<link rel="icon" href="../favicon.svg">
|
||||
<link rel="shortcut icon" href="../favicon.png">
|
||||
<link rel="stylesheet" href="../css/variables.css">
|
||||
<link rel="stylesheet" href="../css/general.css">
|
||||
<link rel="stylesheet" href="../css/chrome.css">
|
||||
<link rel="stylesheet" href="../css/print.css" media="print">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
|
||||
<link rel="stylesheet" href="../fonts/fonts.css">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" href="../highlight.css">
|
||||
<link rel="stylesheet" href="../tomorrow-night.css">
|
||||
<link rel="stylesheet" href="../ayu-highlight.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
|
||||
<!-- MathJax -->
|
||||
<script async type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Provide site root to javascript -->
|
||||
<script type="text/javascript">
|
||||
var path_to_root = "../";
|
||||
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "navy" : "light";
|
||||
</script>
|
||||
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script type="text/javascript">
|
||||
try {
|
||||
var theme = localStorage.getItem('mdbook-theme');
|
||||
var sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script type="text/javascript">
|
||||
var theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
var html = document.querySelector('html');
|
||||
html.classList.remove('no-js')
|
||||
html.classList.remove('light')
|
||||
html.classList.add(theme);
|
||||
html.classList.add('js');
|
||||
</script>
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script type="text/javascript">
|
||||
var html = document.querySelector('html');
|
||||
var sidebar = 'hidden';
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
}
|
||||
html.classList.remove('sidebar-visible');
|
||||
html.classList.add("sidebar-" + sidebar);
|
||||
</script>
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<div class="sidebar-scrollbox">
|
||||
<ol class="chapter"><li class="chapter-item expanded affix "><a href="../index.html">Introduction</a></li><li class="chapter-item expanded "><a href="../getting_started/index.html"><strong aria-hidden="true">1.</strong> Getting Started</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../getting_started/desktop.html"><strong aria-hidden="true">1.1.</strong> Desktop</a></li><li class="chapter-item expanded "><a href="../getting_started/web.html"><strong aria-hidden="true">1.2.</strong> Web</a></li><li class="chapter-item expanded "><a href="../getting_started/ssr.html"><strong aria-hidden="true">1.3.</strong> Server-Side Rendering</a></li><li class="chapter-item expanded "><a href="../getting_started/fullstack.html"><strong aria-hidden="true">1.4.</strong> Fullstack</a></li><li class="chapter-item expanded "><a href="../getting_started/liveview.html"><strong aria-hidden="true">1.5.</strong> Liveview</a></li><li class="chapter-item expanded "><a href="../getting_started/tui.html"><strong aria-hidden="true">1.6.</strong> Terminal UI</a></li><li class="chapter-item expanded "><a href="../getting_started/mobile.html"><strong aria-hidden="true">1.7.</strong> Mobile</a></li><li class="chapter-item expanded "><a href="../getting_started/hot_reload.html"><strong aria-hidden="true">1.8.</strong> Hot Reloading</a></li></ol></li><li class="chapter-item expanded "><a href="../describing_ui/index.html"><strong aria-hidden="true">2.</strong> Describing the UI</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../describing_ui/special_attributes.html"><strong aria-hidden="true">2.1.</strong> Special Attributes</a></li><li class="chapter-item expanded "><a href="../describing_ui/components.html"><strong aria-hidden="true">2.2.</strong> Components</a></li><li class="chapter-item expanded "><a href="../describing_ui/component_props.html"><strong aria-hidden="true">2.3.</strong> Props</a></li><li class="chapter-item expanded "><a href="../describing_ui/component_children.html"><strong aria-hidden="true">2.4.</strong> Component Children</a></li></ol></li><li class="chapter-item expanded "><a href="../interactivity/index.html" class="active"><strong aria-hidden="true">3.</strong> Interactivity</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../interactivity/event_handlers.html"><strong aria-hidden="true">3.1.</strong> Event Listeners</a></li><li class="chapter-item expanded "><a href="../interactivity/hooks.html"><strong aria-hidden="true">3.2.</strong> Hooks & Component State</a></li><li class="chapter-item expanded "><a href="../interactivity/user_input.html"><strong aria-hidden="true">3.3.</strong> User Input</a></li><li class="chapter-item expanded "><a href="../interactivity/sharing_state.html"><strong aria-hidden="true">3.4.</strong> Sharing State</a></li><li class="chapter-item expanded "><a href="../interactivity/custom_hooks.html"><strong aria-hidden="true">3.5.</strong> Custom Hooks</a></li><li class="chapter-item expanded "><a href="../interactivity/dynamic_rendering.html"><strong aria-hidden="true">3.6.</strong> Dynamic Rendering</a></li><li class="chapter-item expanded "><a href="../interactivity/router.html"><strong aria-hidden="true">3.7.</strong> Routing</a></li></ol></li><li class="chapter-item expanded "><a href="../async/index.html"><strong aria-hidden="true">4.</strong> Async</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../async/use_future.html"><strong aria-hidden="true">4.1.</strong> UseFuture</a></li><li class="chapter-item expanded "><a href="../async/use_coroutine.html"><strong aria-hidden="true">4.2.</strong> UseCoroutine</a></li><li class="chapter-item expanded "><a href="../async/spawn.html"><strong aria-hidden="true">4.3.</strong> Spawning Futures</a></li></ol></li><li class="chapter-item expanded "><a href="../best_practices/index.html"><strong aria-hidden="true">5.</strong> Best Practices</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../best_practices/error_handling.html"><strong aria-hidden="true">5.1.</strong> Error Handling</a></li><li class="chapter-item expanded "><a href="../best_practices/antipatterns.html"><strong aria-hidden="true">5.2.</strong> Antipatterns</a></li></ol></li><li class="chapter-item expanded "><a href="../publishing/index.html"><strong aria-hidden="true">6.</strong> Publishing</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../publishing/desktop.html"><strong aria-hidden="true">6.1.</strong> Desktop</a></li><li class="chapter-item expanded "><a href="../publishing/web.html"><strong aria-hidden="true">6.2.</strong> Web</a></li><li class="spacer"></li></ol></li><li class="chapter-item expanded "><a href="../fullstack/index.html"><strong aria-hidden="true">7.</strong> Fullstack</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../fullstack/getting_started.html"><stLine truncated
|
||||
</div>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
<div id="menu-bar-hover-placeholder"></div>
|
||||
<div id="menu-bar" class="menu-bar sticky bordered">
|
||||
<div class="left-buttons">
|
||||
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<i class="fa fa-bars"></i>
|
||||
</button>
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<i class="fa fa-paint-brush"></i>
|
||||
</button>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="light">Light (default)</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||
</ul>
|
||||
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
<button id="language-toggle" class="icon-button" type="button" title="Select language" aria-label="Select language" aria-haspopup="true" aria-expanded="false" aria-controls="language-list">
|
||||
<i class="fa fa-globe"></i>
|
||||
</button>
|
||||
<ul id="language-list" class="language-popup" aria-label="Languages" role="menu">
|
||||
<li role="none"><a href="../../en/interactivity/index.html"><button role="menuitem" class="language" id="light">English</button></a></li>
|
||||
<li role="none"><a href="../../pt-br/interactivity/index.html"><button role="menuitem" class="language" id="light">Português Brasileiro</button></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title"></h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
||||
<i id="print-button" class="fa fa-print"></i>
|
||||
</a>
|
||||
<a href="https://github.com/DioxusLabs/dioxus/edit/master/docs/guide" title="Git repository" aria-label="Git repository">
|
||||
<i id="git-repository-button" class="fa fa-github"></i>
|
||||
</a>
|
||||
<a href="https://github.com/DioxusLabs/dioxus/edit/master/docs/guide/src/interactivity/index.md" title="Suggest an edit" aria-label="Suggest an edit">
|
||||
<i id="git-edit-button" class="fa fa-edit"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="search-wrapper" class="hidden">
|
||||
<form id="searchbar-outer" class="searchbar-outer">
|
||||
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||
</form>
|
||||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script type="text/javascript">
|
||||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1 id="interactivity"><a class="header" href="#interactivity">Interactivity</a></h1>
|
||||
<p>So far, we've learned how to describe the structure and properties of our user interfaces. However, most interfaces need to be interactive in order to be useful. In this chapter, we describe how to make a Dioxus app that responds to the user.</p>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../describing_ui/component_children.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next" href="../interactivity/event_handlers.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../describing_ui/component_children.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next" href="../interactivity/event_handlers.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
window.playground_line_numbers = true;
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
window.playground_copyable = true;
|
||||
</script>
|
||||
|
||||
<script src="../ace.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../editor.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../mode-rust.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../theme-dawn.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../theme-tomorrow_night.js" type="text/javascript" charset="utf-8"></script>
|
||||
|
||||
<script src="../elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../mark.min.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../searcher.js" type="text/javascript" charset="utf-8"></script>
|
||||
|
||||
<script src="../clipboard.min.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../highlight.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../book.js" type="text/javascript" charset="utf-8"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,296 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="sidebar-visible no-js light">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>Routing</title>
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff" />
|
||||
|
||||
<link rel="icon" href="../favicon.svg">
|
||||
<link rel="shortcut icon" href="../favicon.png">
|
||||
<link rel="stylesheet" href="../css/variables.css">
|
||||
<link rel="stylesheet" href="../css/general.css">
|
||||
<link rel="stylesheet" href="../css/chrome.css">
|
||||
<link rel="stylesheet" href="../css/print.css" media="print">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
|
||||
<link rel="stylesheet" href="../fonts/fonts.css">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" href="../highlight.css">
|
||||
<link rel="stylesheet" href="../tomorrow-night.css">
|
||||
<link rel="stylesheet" href="../ayu-highlight.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
|
||||
<!-- MathJax -->
|
||||
<script async type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Provide site root to javascript -->
|
||||
<script type="text/javascript">
|
||||
var path_to_root = "../";
|
||||
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "navy" : "light";
|
||||
</script>
|
||||
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script type="text/javascript">
|
||||
try {
|
||||
var theme = localStorage.getItem('mdbook-theme');
|
||||
var sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script type="text/javascript">
|
||||
var theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
var html = document.querySelector('html');
|
||||
html.classList.remove('no-js')
|
||||
html.classList.remove('light')
|
||||
html.classList.add(theme);
|
||||
html.classList.add('js');
|
||||
</script>
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script type="text/javascript">
|
||||
var html = document.querySelector('html');
|
||||
var sidebar = 'hidden';
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
}
|
||||
html.classList.remove('sidebar-visible');
|
||||
html.classList.add("sidebar-" + sidebar);
|
||||
</script>
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<div class="sidebar-scrollbox">
|
||||
<ol class="chapter"><li class="chapter-item expanded affix "><a href="../index.html">Introduction</a></li><li class="chapter-item expanded "><a href="../getting_started/index.html"><strong aria-hidden="true">1.</strong> Getting Started</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../getting_started/desktop.html"><strong aria-hidden="true">1.1.</strong> Desktop</a></li><li class="chapter-item expanded "><a href="../getting_started/web.html"><strong aria-hidden="true">1.2.</strong> Web</a></li><li class="chapter-item expanded "><a href="../getting_started/ssr.html"><strong aria-hidden="true">1.3.</strong> Server-Side Rendering</a></li><li class="chapter-item expanded "><a href="../getting_started/fullstack.html"><strong aria-hidden="true">1.4.</strong> Fullstack</a></li><li class="chapter-item expanded "><a href="../getting_started/liveview.html"><strong aria-hidden="true">1.5.</strong> Liveview</a></li><li class="chapter-item expanded "><a href="../getting_started/tui.html"><strong aria-hidden="true">1.6.</strong> Terminal UI</a></li><li class="chapter-item expanded "><a href="../getting_started/mobile.html"><strong aria-hidden="true">1.7.</strong> Mobile</a></li><li class="chapter-item expanded "><a href="../getting_started/hot_reload.html"><strong aria-hidden="true">1.8.</strong> Hot Reloading</a></li></ol></li><li class="chapter-item expanded "><a href="../describing_ui/index.html"><strong aria-hidden="true">2.</strong> Describing the UI</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../describing_ui/special_attributes.html"><strong aria-hidden="true">2.1.</strong> Special Attributes</a></li><li class="chapter-item expanded "><a href="../describing_ui/components.html"><strong aria-hidden="true">2.2.</strong> Components</a></li><li class="chapter-item expanded "><a href="../describing_ui/component_props.html"><strong aria-hidden="true">2.3.</strong> Props</a></li><li class="chapter-item expanded "><a href="../describing_ui/component_children.html"><strong aria-hidden="true">2.4.</strong> Component Children</a></li></ol></li><li class="chapter-item expanded "><a href="../interactivity/index.html"><strong aria-hidden="true">3.</strong> Interactivity</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../interactivity/event_handlers.html"><strong aria-hidden="true">3.1.</strong> Event Listeners</a></li><li class="chapter-item expanded "><a href="../interactivity/hooks.html"><strong aria-hidden="true">3.2.</strong> Hooks & Component State</a></li><li class="chapter-item expanded "><a href="../interactivity/user_input.html"><strong aria-hidden="true">3.3.</strong> User Input</a></li><li class="chapter-item expanded "><a href="../interactivity/sharing_state.html"><strong aria-hidden="true">3.4.</strong> Sharing State</a></li><li class="chapter-item expanded "><a href="../interactivity/custom_hooks.html"><strong aria-hidden="true">3.5.</strong> Custom Hooks</a></li><li class="chapter-item expanded "><a href="../interactivity/dynamic_rendering.html"><strong aria-hidden="true">3.6.</strong> Dynamic Rendering</a></li><li class="chapter-item expanded "><a href="../interactivity/router.html" class="active"><strong aria-hidden="true">3.7.</strong> Routing</a></li></ol></li><li class="chapter-item expanded "><a href="../async/index.html"><strong aria-hidden="true">4.</strong> Async</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../async/use_future.html"><strong aria-hidden="true">4.1.</strong> UseFuture</a></li><li class="chapter-item expanded "><a href="../async/use_coroutine.html"><strong aria-hidden="true">4.2.</strong> UseCoroutine</a></li><li class="chapter-item expanded "><a href="../async/spawn.html"><strong aria-hidden="true">4.3.</strong> Spawning Futures</a></li></ol></li><li class="chapter-item expanded "><a href="../best_practices/index.html"><strong aria-hidden="true">5.</strong> Best Practices</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../best_practices/error_handling.html"><strong aria-hidden="true">5.1.</strong> Error Handling</a></li><li class="chapter-item expanded "><a href="../best_practices/antipatterns.html"><strong aria-hidden="true">5.2.</strong> Antipatterns</a></li></ol></li><li class="chapter-item expanded "><a href="../publishing/index.html"><strong aria-hidden="true">6.</strong> Publishing</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../publishing/desktop.html"><strong aria-hidden="true">6.1.</strong> Desktop</a></li><li class="chapter-item expanded "><a href="../publishing/web.html"><strong aria-hidden="true">6.2.</strong> Web</a></li><li class="spacer"></li></ol></li><li class="chapter-item expanded "><a href="../fullstack/index.html"><strong aria-hidden="true">7.</strong> Fullstack</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../fullstack/getting_started.html"><stLine truncated
|
||||
</div>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
<div id="menu-bar-hover-placeholder"></div>
|
||||
<div id="menu-bar" class="menu-bar sticky bordered">
|
||||
<div class="left-buttons">
|
||||
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<i class="fa fa-bars"></i>
|
||||
</button>
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<i class="fa fa-paint-brush"></i>
|
||||
</button>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="light">Light (default)</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||
</ul>
|
||||
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
<button id="language-toggle" class="icon-button" type="button" title="Select language" aria-label="Select language" aria-haspopup="true" aria-expanded="false" aria-controls="language-list">
|
||||
<i class="fa fa-globe"></i>
|
||||
</button>
|
||||
<ul id="language-list" class="language-popup" aria-label="Languages" role="menu">
|
||||
<li role="none"><a href="../../en/interactivity/router.html"><button role="menuitem" class="language" id="light">English</button></a></li>
|
||||
<li role="none"><a href="../../pt-br/interactivity/router.html"><button role="menuitem" class="language" id="light">Português Brasileiro</button></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title"></h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
||||
<i id="print-button" class="fa fa-print"></i>
|
||||
</a>
|
||||
<a href="https://github.com/DioxusLabs/dioxus/edit/master/docs/guide" title="Git repository" aria-label="Git repository">
|
||||
<i id="git-repository-button" class="fa fa-github"></i>
|
||||
</a>
|
||||
<a href="https://github.com/DioxusLabs/dioxus/edit/master/docs/guide/src/interactivity/router.md" title="Suggest an edit" aria-label="Suggest an edit">
|
||||
<i id="git-edit-button" class="fa fa-edit"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="search-wrapper" class="hidden">
|
||||
<form id="searchbar-outer" class="searchbar-outer">
|
||||
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||
</form>
|
||||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script type="text/javascript">
|
||||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1 id="router"><a class="header" href="#router">Router</a></h1>
|
||||
<p>In many of your apps, you'll want to have different "scenes". For a webpage, these scenes might be the different webpages with their own content. For a desktop app, these scenes might be different views in your app.</p>
|
||||
<p>To unify these platforms, Dioxus provides a first-party solution for scene management called Dioxus Router.</p>
|
||||
<h2 id="what-is-it"><a class="header" href="#what-is-it">What is it?</a></h2>
|
||||
<p>For an app like the Dioxus landing page (https://dioxuslabs.com), we want to have several different scenes:</p>
|
||||
<ul>
|
||||
<li>Homepage</li>
|
||||
<li>Blog</li>
|
||||
</ul>
|
||||
<p>Each of these scenes is independent – we don't want to render both the homepage and blog at the same time.</p>
|
||||
<p>The Dioxus router makes it easy to create these scenes. To make sure we're using the router, add the <code>dioxus-router</code> package to your <code>Cargo.toml</code>.</p>
|
||||
<pre><code class="language-shell">cargo add dioxus-router
|
||||
</code></pre>
|
||||
<h2 id="using-the-router"><a class="header" href="#using-the-router">Using the router</a></h2>
|
||||
<p>Unlike other routers in the Rust ecosystem, our router is built declaratively. This makes it possible to compose our app layout simply by arranging components.</p>
|
||||
<pre><pre class="playground"><code class="language-rust edition2018">
|
||||
<span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>rsx!{
|
||||
// All of our routes will be rendered inside this Router component
|
||||
Router {
|
||||
// if the current location is "/home", render the Home component
|
||||
Route { to: "/home", Home {} }
|
||||
// if the current location is "/blog", render the Blog component
|
||||
Route { to: "/blog", Blog {} }
|
||||
}
|
||||
}
|
||||
<span class="boring">}
|
||||
</span></code></pre></pre>
|
||||
<p>Whenever we visit this app, we will get either the Home component or the Blog component rendered depending on which route we enter at. If neither of these routes match the current location, then nothing will render.</p>
|
||||
<p>We can fix this one of two ways:</p>
|
||||
<ul>
|
||||
<li>A fallback 404 page</li>
|
||||
</ul>
|
||||
<pre><pre class="playground"><code class="language-rust edition2018">
|
||||
<span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>rsx!{
|
||||
Router {
|
||||
Route { to: "/home", Home {} }
|
||||
Route { to: "/blog", Blog {} }
|
||||
// if the current location doesn't match any of the above routes, render the NotFound component
|
||||
Route { to: "", NotFound {} }
|
||||
}
|
||||
}
|
||||
<span class="boring">}
|
||||
</span></code></pre></pre>
|
||||
<ul>
|
||||
<li>Redirect 404 to home</li>
|
||||
</ul>
|
||||
<pre><pre class="playground"><code class="language-rust edition2018">
|
||||
<span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>rsx!{
|
||||
Router {
|
||||
Route { to: "/home", Home {} }
|
||||
Route { to: "/blog", Blog {} }
|
||||
// if the current location doesn't match any of the above routes, redirect to "/home"
|
||||
Redirect { from: "", to: "/home" }
|
||||
}
|
||||
}
|
||||
<span class="boring">}
|
||||
</span></code></pre></pre>
|
||||
<h2 id="links"><a class="header" href="#links">Links</a></h2>
|
||||
<p>For our app to navigate these routes, we can provide clickable elements called Links. These simply wrap <code><a></code> elements that, when clicked, navigate the app to the given location.</p>
|
||||
<pre><pre class="playground"><code class="language-rust edition2018">
|
||||
<span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>rsx!{
|
||||
Link {
|
||||
to: "/home",
|
||||
"Go home!"
|
||||
}
|
||||
}
|
||||
<span class="boring">}
|
||||
</span></code></pre></pre>
|
||||
<h2 id="more-reading"><a class="header" href="#more-reading">More reading</a></h2>
|
||||
<p>This page is just a very brief overview of the router. For more information, check out <a href="https://dioxuslabs.com/docs/0.3/router/">the router book</a> or some of <a href="https://github.com/DioxusLabs/dioxus/blob/master/examples/router.rs">the router examples</a>.</p>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../interactivity/dynamic_rendering.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next" href="../async/index.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../interactivity/dynamic_rendering.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next" href="../async/index.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
window.playground_line_numbers = true;
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
window.playground_copyable = true;
|
||||
</script>
|
||||
|
||||
<script src="../ace.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../editor.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../mode-rust.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../theme-dawn.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../theme-tomorrow_night.js" type="text/javascript" charset="utf-8"></script>
|
||||
|
||||
<script src="../elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../mark.min.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../searcher.js" type="text/javascript" charset="utf-8"></script>
|
||||
|
||||
<script src="../clipboard.min.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../highlight.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../book.js" type="text/javascript" charset="utf-8"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,392 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="sidebar-visible no-js light">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>Sharing State</title>
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff" />
|
||||
|
||||
<link rel="icon" href="../favicon.svg">
|
||||
<link rel="shortcut icon" href="../favicon.png">
|
||||
<link rel="stylesheet" href="../css/variables.css">
|
||||
<link rel="stylesheet" href="../css/general.css">
|
||||
<link rel="stylesheet" href="../css/chrome.css">
|
||||
<link rel="stylesheet" href="../css/print.css" media="print">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
|
||||
<link rel="stylesheet" href="../fonts/fonts.css">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" href="../highlight.css">
|
||||
<link rel="stylesheet" href="../tomorrow-night.css">
|
||||
<link rel="stylesheet" href="../ayu-highlight.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
|
||||
<!-- MathJax -->
|
||||
<script async type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Provide site root to javascript -->
|
||||
<script type="text/javascript">
|
||||
var path_to_root = "../";
|
||||
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "navy" : "light";
|
||||
</script>
|
||||
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script type="text/javascript">
|
||||
try {
|
||||
var theme = localStorage.getItem('mdbook-theme');
|
||||
var sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script type="text/javascript">
|
||||
var theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
var html = document.querySelector('html');
|
||||
html.classList.remove('no-js')
|
||||
html.classList.remove('light')
|
||||
html.classList.add(theme);
|
||||
html.classList.add('js');
|
||||
</script>
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script type="text/javascript">
|
||||
var html = document.querySelector('html');
|
||||
var sidebar = 'hidden';
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
}
|
||||
html.classList.remove('sidebar-visible');
|
||||
html.classList.add("sidebar-" + sidebar);
|
||||
</script>
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<div class="sidebar-scrollbox">
|
||||
<ol class="chapter"><li class="chapter-item expanded affix "><a href="../index.html">Introduction</a></li><li class="chapter-item expanded "><a href="../getting_started/index.html"><strong aria-hidden="true">1.</strong> Getting Started</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../getting_started/desktop.html"><strong aria-hidden="true">1.1.</strong> Desktop</a></li><li class="chapter-item expanded "><a href="../getting_started/web.html"><strong aria-hidden="true">1.2.</strong> Web</a></li><li class="chapter-item expanded "><a href="../getting_started/ssr.html"><strong aria-hidden="true">1.3.</strong> Server-Side Rendering</a></li><li class="chapter-item expanded "><a href="../getting_started/fullstack.html"><strong aria-hidden="true">1.4.</strong> Fullstack</a></li><li class="chapter-item expanded "><a href="../getting_started/liveview.html"><strong aria-hidden="true">1.5.</strong> Liveview</a></li><li class="chapter-item expanded "><a href="../getting_started/tui.html"><strong aria-hidden="true">1.6.</strong> Terminal UI</a></li><li class="chapter-item expanded "><a href="../getting_started/mobile.html"><strong aria-hidden="true">1.7.</strong> Mobile</a></li><li class="chapter-item expanded "><a href="../getting_started/hot_reload.html"><strong aria-hidden="true">1.8.</strong> Hot Reloading</a></li></ol></li><li class="chapter-item expanded "><a href="../describing_ui/index.html"><strong aria-hidden="true">2.</strong> Describing the UI</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../describing_ui/special_attributes.html"><strong aria-hidden="true">2.1.</strong> Special Attributes</a></li><li class="chapter-item expanded "><a href="../describing_ui/components.html"><strong aria-hidden="true">2.2.</strong> Components</a></li><li class="chapter-item expanded "><a href="../describing_ui/component_props.html"><strong aria-hidden="true">2.3.</strong> Props</a></li><li class="chapter-item expanded "><a href="../describing_ui/component_children.html"><strong aria-hidden="true">2.4.</strong> Component Children</a></li></ol></li><li class="chapter-item expanded "><a href="../interactivity/index.html"><strong aria-hidden="true">3.</strong> Interactivity</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../interactivity/event_handlers.html"><strong aria-hidden="true">3.1.</strong> Event Listeners</a></li><li class="chapter-item expanded "><a href="../interactivity/hooks.html"><strong aria-hidden="true">3.2.</strong> Hooks & Component State</a></li><li class="chapter-item expanded "><a href="../interactivity/user_input.html"><strong aria-hidden="true">3.3.</strong> User Input</a></li><li class="chapter-item expanded "><a href="../interactivity/sharing_state.html" class="active"><strong aria-hidden="true">3.4.</strong> Sharing State</a></li><li class="chapter-item expanded "><a href="../interactivity/custom_hooks.html"><strong aria-hidden="true">3.5.</strong> Custom Hooks</a></li><li class="chapter-item expanded "><a href="../interactivity/dynamic_rendering.html"><strong aria-hidden="true">3.6.</strong> Dynamic Rendering</a></li><li class="chapter-item expanded "><a href="../interactivity/router.html"><strong aria-hidden="true">3.7.</strong> Routing</a></li></ol></li><li class="chapter-item expanded "><a href="../async/index.html"><strong aria-hidden="true">4.</strong> Async</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../async/use_future.html"><strong aria-hidden="true">4.1.</strong> UseFuture</a></li><li class="chapter-item expanded "><a href="../async/use_coroutine.html"><strong aria-hidden="true">4.2.</strong> UseCoroutine</a></li><li class="chapter-item expanded "><a href="../async/spawn.html"><strong aria-hidden="true">4.3.</strong> Spawning Futures</a></li></ol></li><li class="chapter-item expanded "><a href="../best_practices/index.html"><strong aria-hidden="true">5.</strong> Best Practices</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../best_practices/error_handling.html"><strong aria-hidden="true">5.1.</strong> Error Handling</a></li><li class="chapter-item expanded "><a href="../best_practices/antipatterns.html"><strong aria-hidden="true">5.2.</strong> Antipatterns</a></li></ol></li><li class="chapter-item expanded "><a href="../publishing/index.html"><strong aria-hidden="true">6.</strong> Publishing</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../publishing/desktop.html"><strong aria-hidden="true">6.1.</strong> Desktop</a></li><li class="chapter-item expanded "><a href="../publishing/web.html"><strong aria-hidden="true">6.2.</strong> Web</a></li><li class="spacer"></li></ol></li><li class="chapter-item expanded "><a href="../fullstack/index.html"><strong aria-hidden="true">7.</strong> Fullstack</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../fullstack/getting_started.html"><stLine truncated
|
||||
</div>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
<div id="menu-bar-hover-placeholder"></div>
|
||||
<div id="menu-bar" class="menu-bar sticky bordered">
|
||||
<div class="left-buttons">
|
||||
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<i class="fa fa-bars"></i>
|
||||
</button>
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<i class="fa fa-paint-brush"></i>
|
||||
</button>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="light">Light (default)</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||
</ul>
|
||||
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
<button id="language-toggle" class="icon-button" type="button" title="Select language" aria-label="Select language" aria-haspopup="true" aria-expanded="false" aria-controls="language-list">
|
||||
<i class="fa fa-globe"></i>
|
||||
</button>
|
||||
<ul id="language-list" class="language-popup" aria-label="Languages" role="menu">
|
||||
<li role="none"><a href="../../en/interactivity/sharing_state.html"><button role="menuitem" class="language" id="light">English</button></a></li>
|
||||
<li role="none"><a href="../../pt-br/interactivity/sharing_state.html"><button role="menuitem" class="language" id="light">Português Brasileiro</button></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title"></h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
||||
<i id="print-button" class="fa fa-print"></i>
|
||||
</a>
|
||||
<a href="https://github.com/DioxusLabs/dioxus/edit/master/docs/guide" title="Git repository" aria-label="Git repository">
|
||||
<i id="git-repository-button" class="fa fa-github"></i>
|
||||
</a>
|
||||
<a href="https://github.com/DioxusLabs/dioxus/edit/master/docs/guide/src/interactivity/sharing_state.md" title="Suggest an edit" aria-label="Suggest an edit">
|
||||
<i id="git-edit-button" class="fa fa-edit"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="search-wrapper" class="hidden">
|
||||
<form id="searchbar-outer" class="searchbar-outer">
|
||||
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||
</form>
|
||||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script type="text/javascript">
|
||||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1 id="sharing-state"><a class="header" href="#sharing-state">Sharing State</a></h1>
|
||||
<p>Often, multiple components need to access the same state. Depending on your needs, there are several ways to implement this.</p>
|
||||
<h2 id="lifting-state"><a class="header" href="#lifting-state">Lifting State</a></h2>
|
||||
<p>One approach to share state between components is to "lift" it up to the nearest common ancestor. This means putting the <code>use_state</code> hook in a parent component, and passing the needed values down as props.</p>
|
||||
<p>Suppose we want to build a meme editor. We want to have an input to edit the meme caption, but also a preview of the meme with the caption. Logically, the meme and the input are 2 separate components, but they need access to the same state (the current caption).</p>
|
||||
<blockquote>
|
||||
<p>Of course, in this simple example, we could write everything in one component – but it is better to split everything out in smaller components to make the code more reusable, maintainable, and performant (this is even more important for larger, complex apps).</p>
|
||||
</blockquote>
|
||||
<p>We start with a <code>Meme</code> component, responsible for rendering a meme with a given caption:</p>
|
||||
<pre><pre class="playground"><code class="language-rust edition2018">
|
||||
<span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>#[inline_props]
|
||||
fn Meme<'a>(cx: Scope<'a>, caption: &'a str) -> Element<'a> {
|
||||
let container_style = r#"
|
||||
position: relative;
|
||||
width: fit-content;
|
||||
"#;
|
||||
|
||||
let caption_container_style = r#"
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding: 16px 8px;
|
||||
"#;
|
||||
|
||||
let caption_style = r"
|
||||
font-size: 32px;
|
||||
margin: 0;
|
||||
color: white;
|
||||
text-align: center;
|
||||
";
|
||||
|
||||
cx.render(rsx!(
|
||||
div {
|
||||
style: "{container_style}",
|
||||
img {
|
||||
src: "https://i.imgflip.com/2zh47r.jpg",
|
||||
height: "500px",
|
||||
},
|
||||
div {
|
||||
style: "{caption_container_style}",
|
||||
p {
|
||||
style: "{caption_style}",
|
||||
"{caption}"
|
||||
}
|
||||
}
|
||||
}
|
||||
))
|
||||
}
|
||||
<span class="boring">}
|
||||
</span></code></pre></pre>
|
||||
<blockquote>
|
||||
<p>Note that the <code>Meme</code> component is unaware where the caption is coming from – it could be stored in <code>use_state</code>, <code>use_ref</code>, or a constant. This ensures that it is very reusable – the same component can be used for a meme gallery without any changes!</p>
|
||||
</blockquote>
|
||||
<p>We also create a caption editor, completely decoupled from the meme. The caption editor must not store the caption itself – otherwise, how will we provide it to the <code>Meme</code> component? Instead, it should accept the current caption as a prop, as well as an event handler to delegate input events to:</p>
|
||||
<pre><pre class="playground"><code class="language-rust edition2018">
|
||||
<span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>#[inline_props]
|
||||
fn CaptionEditor<'a>(
|
||||
cx: Scope<'a>,
|
||||
caption: &'a str,
|
||||
on_input: EventHandler<'a, FormEvent>,
|
||||
) -> Element<'a> {
|
||||
let input_style = r"
|
||||
border: none;
|
||||
background: cornflowerblue;
|
||||
padding: 8px 16px;
|
||||
margin: 0;
|
||||
border-radius: 4px;
|
||||
color: white;
|
||||
";
|
||||
|
||||
cx.render(rsx!(input {
|
||||
style: "{input_style}",
|
||||
value: "{caption}",
|
||||
oninput: move |event| on_input.call(event),
|
||||
}))
|
||||
}
|
||||
<span class="boring">}
|
||||
</span></code></pre></pre>
|
||||
<p>Finally, a third component will render the other two as children. It will be responsible for keeping the state and passing down the relevant props.</p>
|
||||
<pre><pre class="playground"><code class="language-rust edition2018">
|
||||
<span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>fn MemeEditor(cx: Scope) -> Element {
|
||||
let container_style = r"
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
margin: 0 auto;
|
||||
width: fit-content;
|
||||
";
|
||||
|
||||
let caption = use_state(cx, || "me waiting for my rust code to compile".to_string());
|
||||
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
style: "{container_style}",
|
||||
h1 { "Meme Editor" },
|
||||
Meme {
|
||||
caption: caption,
|
||||
},
|
||||
CaptionEditor {
|
||||
caption: caption,
|
||||
on_input: move |event: FormEvent| {caption.set(event.value.clone());},
|
||||
},
|
||||
}
|
||||
})
|
||||
}
|
||||
<span class="boring">}
|
||||
</span></code></pre></pre>
|
||||
<p><img src="./images/meme_editor_screenshot.png" alt="Meme Editor Screenshot: An old plastic skeleton sitting on a park bench. Caption: "me waiting for a language feature"" /></p>
|
||||
<h2 id="using-context"><a class="header" href="#using-context">Using Context</a></h2>
|
||||
<p>Sometimes, some state needs to be shared between multiple components far down the tree, and passing it down through props is very inconvenient.</p>
|
||||
<p>Suppose now that we want to implement a dark mode toggle for our app. To achieve this, we will make every component select styling depending on whether dark mode is enabled or not.</p>
|
||||
<blockquote>
|
||||
<p>Note: we're choosing this approach for the sake of an example. There are better ways to implement dark mode (e.g. using CSS variables). Let's pretend CSS variables don't exist – welcome to 2013!</p>
|
||||
</blockquote>
|
||||
<p>Now, we could write another <code>use_state</code> in the top component, and pass <code>is_dark_mode</code> down to every component through props. But think about what will happen as the app grows in complexity – almost every component that renders any CSS is going to need to know if dark mode is enabled or not – so they'll all need the same dark mode prop. And every parent component will need to pass it down to them. Imagine how messy and verbose that would get, especially if we had components several levels deep!</p>
|
||||
<p>Dioxus offers a better solution than this "prop drilling" – providing context. The <a href="https://docs.rs/dioxus-hooks/latest/dioxus_hooks/fn.use_context_provider.html"><code>use_context_provider</code></a> hook is similar to <code>use_ref</code>, but it makes it available through <a href="https://docs.rs/dioxus-hooks/latest/dioxus_hooks/fn.use_context.html"><code>use_context</code></a> for all children components.</p>
|
||||
<p>First, we have to create a struct for our dark mode configuration:</p>
|
||||
<pre><pre class="playground"><code class="language-rust edition2018">
|
||||
<span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>struct DarkMode(bool);
|
||||
<span class="boring">}
|
||||
</span></code></pre></pre>
|
||||
<p>Now, in a top-level component (like <code>App</code>), we can provide the <code>DarkMode</code> context to all children components:</p>
|
||||
<pre><pre class="playground"><code class="language-rust edition2018">
|
||||
<span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>use_shared_state_provider(cx, || DarkMode(false));
|
||||
<span class="boring">}
|
||||
</span></code></pre></pre>
|
||||
<p>As a result, any child component of <code>App</code> (direct or not), can access the <code>DarkMode</code> context.</p>
|
||||
<pre><pre class="playground"><code class="language-rust edition2018">
|
||||
<span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>let dark_mode_context = use_shared_state::<DarkMode>(cx);
|
||||
<span class="boring">}
|
||||
</span></code></pre></pre>
|
||||
<blockquote>
|
||||
<p><code>use_context</code> returns <code>Option<UseSharedState<DarkMode>></code> here. If the context has been provided, the value is <code>Some(UseSharedState<DarkMode>)</code>, which you can call <code>.read</code> or <code>.write</code> on, similarly to <code>UseRef</code>. Otherwise, the value is <code>None</code>.</p>
|
||||
</blockquote>
|
||||
<p>For example, here's how we would implement the dark mode toggle, which both reads the context (to determine what color it should render) and writes to it (to toggle dark mode):</p>
|
||||
<pre><pre class="playground"><code class="language-rust edition2018">
|
||||
<span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>pub fn DarkModeToggle(cx: Scope) -> Element {
|
||||
let dark_mode = use_shared_state::<DarkMode>(cx).unwrap();
|
||||
|
||||
let style = if dark_mode.read().0 {
|
||||
"color:white"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
|
||||
cx.render(rsx!(label {
|
||||
style: "{style}",
|
||||
"Dark Mode",
|
||||
input {
|
||||
r#type: "checkbox",
|
||||
oninput: move |event| {
|
||||
let is_enabled = event.value == "true";
|
||||
dark_mode.write().0 = is_enabled;
|
||||
},
|
||||
},
|
||||
}))
|
||||
}
|
||||
<span class="boring">}
|
||||
</span></code></pre></pre>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../interactivity/user_input.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next" href="../interactivity/custom_hooks.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../interactivity/user_input.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next" href="../interactivity/custom_hooks.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
window.playground_line_numbers = true;
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
window.playground_copyable = true;
|
||||
</script>
|
||||
|
||||
<script src="../ace.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../editor.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../mode-rust.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../theme-dawn.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../theme-tomorrow_night.js" type="text/javascript" charset="utf-8"></script>
|
||||
|
||||
<script src="../elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../mark.min.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../searcher.js" type="text/javascript" charset="utf-8"></script>
|
||||
|
||||
<script src="../clipboard.min.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../highlight.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../book.js" type="text/javascript" charset="utf-8"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,270 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="sidebar-visible no-js light">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>User Input</title>
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff" />
|
||||
|
||||
<link rel="icon" href="../favicon.svg">
|
||||
<link rel="shortcut icon" href="../favicon.png">
|
||||
<link rel="stylesheet" href="../css/variables.css">
|
||||
<link rel="stylesheet" href="../css/general.css">
|
||||
<link rel="stylesheet" href="../css/chrome.css">
|
||||
<link rel="stylesheet" href="../css/print.css" media="print">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
|
||||
<link rel="stylesheet" href="../fonts/fonts.css">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" href="../highlight.css">
|
||||
<link rel="stylesheet" href="../tomorrow-night.css">
|
||||
<link rel="stylesheet" href="../ayu-highlight.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
|
||||
<!-- MathJax -->
|
||||
<script async type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Provide site root to javascript -->
|
||||
<script type="text/javascript">
|
||||
var path_to_root = "../";
|
||||
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "navy" : "light";
|
||||
</script>
|
||||
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script type="text/javascript">
|
||||
try {
|
||||
var theme = localStorage.getItem('mdbook-theme');
|
||||
var sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script type="text/javascript">
|
||||
var theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
var html = document.querySelector('html');
|
||||
html.classList.remove('no-js')
|
||||
html.classList.remove('light')
|
||||
html.classList.add(theme);
|
||||
html.classList.add('js');
|
||||
</script>
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script type="text/javascript">
|
||||
var html = document.querySelector('html');
|
||||
var sidebar = 'hidden';
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
}
|
||||
html.classList.remove('sidebar-visible');
|
||||
html.classList.add("sidebar-" + sidebar);
|
||||
</script>
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<div class="sidebar-scrollbox">
|
||||
<ol class="chapter"><li class="chapter-item expanded affix "><a href="../index.html">Introduction</a></li><li class="chapter-item expanded "><a href="../getting_started/index.html"><strong aria-hidden="true">1.</strong> Getting Started</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../getting_started/desktop.html"><strong aria-hidden="true">1.1.</strong> Desktop</a></li><li class="chapter-item expanded "><a href="../getting_started/web.html"><strong aria-hidden="true">1.2.</strong> Web</a></li><li class="chapter-item expanded "><a href="../getting_started/ssr.html"><strong aria-hidden="true">1.3.</strong> Server-Side Rendering</a></li><li class="chapter-item expanded "><a href="../getting_started/fullstack.html"><strong aria-hidden="true">1.4.</strong> Fullstack</a></li><li class="chapter-item expanded "><a href="../getting_started/liveview.html"><strong aria-hidden="true">1.5.</strong> Liveview</a></li><li class="chapter-item expanded "><a href="../getting_started/tui.html"><strong aria-hidden="true">1.6.</strong> Terminal UI</a></li><li class="chapter-item expanded "><a href="../getting_started/mobile.html"><strong aria-hidden="true">1.7.</strong> Mobile</a></li><li class="chapter-item expanded "><a href="../getting_started/hot_reload.html"><strong aria-hidden="true">1.8.</strong> Hot Reloading</a></li></ol></li><li class="chapter-item expanded "><a href="../describing_ui/index.html"><strong aria-hidden="true">2.</strong> Describing the UI</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../describing_ui/special_attributes.html"><strong aria-hidden="true">2.1.</strong> Special Attributes</a></li><li class="chapter-item expanded "><a href="../describing_ui/components.html"><strong aria-hidden="true">2.2.</strong> Components</a></li><li class="chapter-item expanded "><a href="../describing_ui/component_props.html"><strong aria-hidden="true">2.3.</strong> Props</a></li><li class="chapter-item expanded "><a href="../describing_ui/component_children.html"><strong aria-hidden="true">2.4.</strong> Component Children</a></li></ol></li><li class="chapter-item expanded "><a href="../interactivity/index.html"><strong aria-hidden="true">3.</strong> Interactivity</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../interactivity/event_handlers.html"><strong aria-hidden="true">3.1.</strong> Event Listeners</a></li><li class="chapter-item expanded "><a href="../interactivity/hooks.html"><strong aria-hidden="true">3.2.</strong> Hooks & Component State</a></li><li class="chapter-item expanded "><a href="../interactivity/user_input.html" class="active"><strong aria-hidden="true">3.3.</strong> User Input</a></li><li class="chapter-item expanded "><a href="../interactivity/sharing_state.html"><strong aria-hidden="true">3.4.</strong> Sharing State</a></li><li class="chapter-item expanded "><a href="../interactivity/custom_hooks.html"><strong aria-hidden="true">3.5.</strong> Custom Hooks</a></li><li class="chapter-item expanded "><a href="../interactivity/dynamic_rendering.html"><strong aria-hidden="true">3.6.</strong> Dynamic Rendering</a></li><li class="chapter-item expanded "><a href="../interactivity/router.html"><strong aria-hidden="true">3.7.</strong> Routing</a></li></ol></li><li class="chapter-item expanded "><a href="../async/index.html"><strong aria-hidden="true">4.</strong> Async</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../async/use_future.html"><strong aria-hidden="true">4.1.</strong> UseFuture</a></li><li class="chapter-item expanded "><a href="../async/use_coroutine.html"><strong aria-hidden="true">4.2.</strong> UseCoroutine</a></li><li class="chapter-item expanded "><a href="../async/spawn.html"><strong aria-hidden="true">4.3.</strong> Spawning Futures</a></li></ol></li><li class="chapter-item expanded "><a href="../best_practices/index.html"><strong aria-hidden="true">5.</strong> Best Practices</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../best_practices/error_handling.html"><strong aria-hidden="true">5.1.</strong> Error Handling</a></li><li class="chapter-item expanded "><a href="../best_practices/antipatterns.html"><strong aria-hidden="true">5.2.</strong> Antipatterns</a></li></ol></li><li class="chapter-item expanded "><a href="../publishing/index.html"><strong aria-hidden="true">6.</strong> Publishing</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../publishing/desktop.html"><strong aria-hidden="true">6.1.</strong> Desktop</a></li><li class="chapter-item expanded "><a href="../publishing/web.html"><strong aria-hidden="true">6.2.</strong> Web</a></li><li class="spacer"></li></ol></li><li class="chapter-item expanded "><a href="../fullstack/index.html"><strong aria-hidden="true">7.</strong> Fullstack</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../fullstack/getting_started.html"><stLine truncated
|
||||
</div>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
<div id="menu-bar-hover-placeholder"></div>
|
||||
<div id="menu-bar" class="menu-bar sticky bordered">
|
||||
<div class="left-buttons">
|
||||
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<i class="fa fa-bars"></i>
|
||||
</button>
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<i class="fa fa-paint-brush"></i>
|
||||
</button>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="light">Light (default)</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||
</ul>
|
||||
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
<button id="language-toggle" class="icon-button" type="button" title="Select language" aria-label="Select language" aria-haspopup="true" aria-expanded="false" aria-controls="language-list">
|
||||
<i class="fa fa-globe"></i>
|
||||
</button>
|
||||
<ul id="language-list" class="language-popup" aria-label="Languages" role="menu">
|
||||
<li role="none"><a href="../../en/interactivity/user_input.html"><button role="menuitem" class="language" id="light">English</button></a></li>
|
||||
<li role="none"><a href="../../pt-br/interactivity/user_input.html"><button role="menuitem" class="language" id="light">Português Brasileiro</button></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title"></h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
||||
<i id="print-button" class="fa fa-print"></i>
|
||||
</a>
|
||||
<a href="https://github.com/DioxusLabs/dioxus/edit/master/docs/guide" title="Git repository" aria-label="Git repository">
|
||||
<i id="git-repository-button" class="fa fa-github"></i>
|
||||
</a>
|
||||
<a href="https://github.com/DioxusLabs/dioxus/edit/master/docs/guide/src/interactivity/user_input.md" title="Suggest an edit" aria-label="Suggest an edit">
|
||||
<i id="git-edit-button" class="fa fa-edit"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="search-wrapper" class="hidden">
|
||||
<form id="searchbar-outer" class="searchbar-outer">
|
||||
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||
</form>
|
||||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script type="text/javascript">
|
||||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1 id="user-input"><a class="header" href="#user-input">User Input</a></h1>
|
||||
<p>Interfaces often need to provide a way to input data: e.g. text, numbers, checkboxes, etc. In Dioxus, there are two ways you can work with user input.</p>
|
||||
<h2 id="controlled-inputs"><a class="header" href="#controlled-inputs">Controlled Inputs</a></h2>
|
||||
<p>With controlled inputs, you are directly in charge of the state of the input. This gives you a lot of flexibility, and makes it easy to keep things in sync. For example, this is how you would create a controlled text input:</p>
|
||||
<pre><pre class="playground"><code class="language-rust edition2018">
|
||||
<span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>fn App(cx: Scope) -> Element {
|
||||
let name = use_state(cx, || "bob".to_string());
|
||||
|
||||
cx.render(rsx! {
|
||||
input {
|
||||
// we tell the component what to render
|
||||
value: "{name}",
|
||||
// and what to do when the value changes
|
||||
oninput: move |evt| name.set(evt.value.clone()),
|
||||
}
|
||||
})
|
||||
}
|
||||
<span class="boring">}
|
||||
</span></code></pre></pre>
|
||||
<p>Notice the flexibility – you can:</p>
|
||||
<ul>
|
||||
<li>Also display the same contents in another element, and they will be in sync</li>
|
||||
<li>Transform the input every time it is modified (e.g. to make sure it is upper case)</li>
|
||||
<li>Validate the input every time it changes</li>
|
||||
<li>Have custom logic happening when the input changes (e.g. network request for autocompletion)</li>
|
||||
<li>Programmatically change the value (e.g. a "randomize" button that fills the input with nonsense)</li>
|
||||
</ul>
|
||||
<h2 id="uncontrolled-inputs"><a class="header" href="#uncontrolled-inputs">Uncontrolled Inputs</a></h2>
|
||||
<p>As an alternative to controlled inputs, you can simply let the platform keep track of the input values. If we don't tell a HTML input what content it should have, it will be editable anyway (this is built into the browser). This approach can be more performant, but less flexible. For example, it's harder to keep the input in sync with another element.</p>
|
||||
<p>Since you don't necessarily have the current value of the uncontrolled input in state, you can access it either by listening to <code>oninput</code> events (similarly to controlled components), or, if the input is part of a form, you can access the form data in the form events (e.g. <code>oninput</code> or <code>onsubmit</code>):</p>
|
||||
<pre><pre class="playground"><code class="language-rust edition2018">
|
||||
<span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>fn App(cx: Scope) -> Element {
|
||||
cx.render(rsx! {
|
||||
form {
|
||||
onsubmit: move |event| {
|
||||
println!("Submitted! {event:?}")
|
||||
},
|
||||
input { name: "name", },
|
||||
input { name: "age", },
|
||||
input { name: "date", },
|
||||
input { r#type: "submit", },
|
||||
}
|
||||
})
|
||||
}
|
||||
<span class="boring">}
|
||||
</span></code></pre></pre>
|
||||
<pre><code>Submitted! UiEvent { data: FormData { value: "", values: {"age": "very old", "date": "1966", "name": "Fred"} } }
|
||||
</code></pre>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../interactivity/hooks.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next" href="../interactivity/sharing_state.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../interactivity/hooks.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next" href="../interactivity/sharing_state.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
window.playground_line_numbers = true;
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
window.playground_copyable = true;
|
||||
</script>
|
||||
|
||||
<script src="../ace.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../editor.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../mode-rust.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../theme-dawn.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../theme-tomorrow_night.js" type="text/javascript" charset="utf-8"></script>
|
||||
|
||||
<script src="../elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../mark.min.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../searcher.js" type="text/javascript" charset="utf-8"></script>
|
||||
|
||||
<script src="../clipboard.min.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../highlight.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="../book.js" type="text/javascript" charset="utf-8"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Reference in new issue
Block a user