Renderização Dinâmica

Às vezes você quer renderizar coisas diferentes dependendo do estado/props. Com o Dioxus, apenas descreva o que você quer ver usando o fluxo de controle do Rust – o framework se encarregará de fazer as mudanças necessárias em tempo real se o estado ou props mudarem!

Renderização Condicional

Para renderizar diferentes elementos com base em uma condição, você pode usar uma instrução if-else:


#![allow(unused)]
fn main() {
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",
        }
    })
}
}

Você também pode usar instruções match, ou qualquer função Rust para renderizar condicionalmente coisas diferentes.

Inspecionando props Element

Como Element é uma Option<VNode>, os componentes que aceitam Element como prop podem realmente inspecionar seu conteúdo e renderizar coisas diferentes com base nisso. Exemplo:


#![allow(unused)]
fn main() {
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")
        }
    }
}
}

Você não pode modificar o Element, mas se precisar de uma versão modificada dele, você pode construir um novo baseado em seus atributos/filhos/etc.

Renderizando Nada

Para renderizar nada, você pode retornar None de um componente. Isso é útil se você deseja ocultar algo condicionalmente:


#![allow(unused)]
fn main() {
if *is_logged_in {
    return None;
}

cx.render(rsx! {
    a {
        "You must be logged in to comment"
    }
})
}

Isso funciona porque o tipo Element é apenas um alias para Option<VNode>

Novamente, você pode usar um método diferente para retornar condicionalmente None. Por exemplo, a função booleana then() pode ser usada.

Listas de renderização

Frequentemente, você desejará renderizar uma coleção de componentes. Por exemplo, você pode querer renderizar uma lista de todos os comentários em uma postagem.

Para isso, o Dioxus aceita iteradores que produzem Elements. Então precisamos:

  • Obter um iterador sobre todos os nossos itens (por exemplo, se você tiver um Vec de comentários, itere sobre ele com iter())
  • .map o iterador para converter cada item em um Element renderizado usando cx.render(rsx!(...))
    • Adicione um atributo key exclusivo a cada item do iterador
  • Incluir este iterador no RSX final

Exemplo: suponha que você tenha uma lista de comentários que deseja renderizar. Então, você pode renderizá-los assim:


#![allow(unused)]
fn main() {
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,
))
}

O Atributo key

Toda vez que você renderiza novamente sua lista, o Dioxus precisa acompanhar qual item foi para onde, porque a ordem dos itens em uma lista pode mudar – itens podem ser adicionados, removidos ou trocados. Apesar disso, Dioxus precisa:

  • Acompanhar o estado do componente
  • Descobrir com eficiência quais atualizações precisam ser feitas na interface do usuário

Por exemplo, suponha que o CommentComponent tenha algum estado – ex. um campo onde o usuário digitou uma resposta. Se a ordem dos comentários mudar repentinamente, o Dioxus precisa associar corretamente esse estado ao mesmo comentário – caso contrário, o usuário acabará respondendo a um comentário diferente!

Para ajudar o Dioxus a acompanhar os itens da lista, precisamos associar cada item a uma chave exclusiva. No exemplo acima, geramos dinamicamente a chave exclusiva. Em aplicações reais, é mais provável que a chave venha de, por exemplo, um ID de banco de dados. Realmente não importa de onde você obtém a chave, desde que atenda aos requisitos

  • As chaves devem ser únicas em uma lista
  • O mesmo item deve sempre ser associado à mesma chave
  • As chaves devem ser relativamente pequenas (ou seja, converter toda a estrutura Comment em uma String seria uma chave muito ruim) para que possam ser comparadas com eficiência

Você pode ficar tentado a usar o índice de um item na lista como sua chave. Na verdade, é isso que o Dioxus usará se você não especificar uma chave. Isso só é aceitável se você puder garantir que a lista seja constante – ou seja, sem reordenação, adições ou exclusões.

Observe que se você passar a chave para um componente que você criou, ele não receberá a chave como prop. É usado apenas como uma dica pelo próprio Dioxus. Se o seu componente precisar de um ID, você deve passá-lo como um prop separado.