如何用Yew创建类型安全的纯无状态输入组件?
Yew 纯无状态类型安全输入组件实现方案
以下是完全符合你需求的实现方式:组件纯无状态、所有配置通过Props传递、无内置验证逻辑,且通过Rust的类型系统保证不同输入类型只能传入对应合法属性。
1. 基础定义与类型枚举
首先定义输入类型枚举,确保类型安全的同时,方便转换为原生input的type属性值:
use yew::prelude::*; #[derive(Clone, PartialEq, Debug)] pub enum InputType { Text, Number, File, Password, Email, } impl ToString for InputType { fn to_string(&self) -> String { match self { InputType::Text => "text".into(), InputType::Number => "number".into(), InputType::File => "file".into(), InputType::Password => "password".into(), InputType::Email => "email".into(), } } }
2. 分类型定义属性结构体
为每种输入类型定义专属的Props结构体,仅包含该类型支持的属性:
#[derive(Clone, PartialEq, Properties)] pub struct TextInputProps { #[prop_or_default] pub placeholder: Option<String>, #[prop_or_default] pub value: Option<String>, #[prop_or_default] pub on_change: Option<Callback<InputEvent>>, #[prop_or_default] pub disabled: bool, #[prop_or_default] pub class: Option<String>, } #[derive(Clone, PartialEq, Properties)] pub struct NumberInputProps { #[prop_or_default] pub placeholder: Option<String>, #[prop_or_default] pub value: Option<String>, #[prop_or_default] pub on_change: Option<Callback<InputEvent>>, #[prop_or_default] pub disabled: bool, #[prop_or_default] pub class: Option<String>, #[prop_or_default] pub max: Option<f64>, #[prop_or_default] pub min: Option<f64>, #[prop_or_default] pub step: Option<f64>, } #[derive(Clone, PartialEq, Properties)] pub struct FileInputProps { #[prop_or_default] pub accept: Option<String>, #[prop_or_default] pub multiple: bool, #[prop_or_default] pub on_change: Option<Callback<InputEvent>>, #[prop_or_default] pub disabled: bool, #[prop_or_default] pub class: Option<String>, }
3. 统一Props枚举
用枚举封装所有输入类型的属性,确保编译时就能检查属性与输入类型的匹配性:
#[derive(Clone, PartialEq, Properties)] pub enum InputProps { Text(TextInputProps), Number(NumberInputProps), File(FileInputProps), }
4. 实现纯无状态组件
组件为纯函数组件,无内部状态,仅根据Props渲染对应的原生input元素:
#[function_component(Input)] pub fn input(props: &InputProps) -> Html { match props { InputProps::Text(text_props) => html! { <input type={InputType::Text.to_string()} placeholder={&text_props.placeholder} value={&text_props.value} disabled={text_props.disabled} class={&text_props.class} on_change={text_props.on_change.clone()} /> }, InputProps::Number(num_props) => html! { <input type={InputType::Number.to_string()} placeholder={&num_props.placeholder} value={&num_props.value} disabled={num_props.disabled} class={&num_props.class} on_change={num_props.on_change.clone()} max={num_props.max.map(|v| v.to_string())} min={num_props.min.map(|v| v.to_string())} step={num_props.step.map(|v| v.to_string())} /> }, InputProps::File(file_props) => html! { <input type={InputType::File.to_string()} accept={&file_props.accept} multiple={file_props.multiple} disabled={file_props.disabled} class={&file_props.class} on_change={file_props.on_change.clone()} /> }, } }
5. 示例用法
完全符合你需求的文本输入框使用方式,以及其他类型的示例:
// 文本输入框(对应你要求的示例) html! { <Input props={InputProps::Text(TextInputProps { placeholder: Some("search:".into()), ..Default::default() })} /> } // 带max/min限制的数字输入框 html! { <Input props={InputProps::Number(NumberInputProps { placeholder: Some("Enter age".into()), min: Some(0.0), max: Some(120.0), ..Default::default() })} /> } // 限制图片类型的多文件输入框 html! { <Input props={InputProps::File(FileInputProps { accept: Some("image/*".into()), multiple: true, ..Default::default() })} /> }
核心特性说明
- 纯无状态:组件无内部状态,所有行为完全由Props驱动
- 类型安全:编译时检查属性与输入类型的匹配性,比如File类型无法传入max/min属性
- 无业务逻辑:组件仅负责渲染,验证、数据处理等逻辑完全由父组件通过
on_change回调实现
内容的提问来源于stack exchange,提问作者dmn28
相关产品推荐
相关产品推荐

