前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >React Native | Radio 组件记录

React Native | Radio 组件记录

原创
作者头像
花花Binki
发布2024-04-04 00:13:38
1172
发布2024-04-04 00:13:38
举报
文章被收录于专栏:岚的工作随笔岚的工作随笔
封面
封面

前言

公司之前一版的手机应用没有做业务、控制分离的处理,导致其他项目参考时,很难复用其中的功能。所以leader决定近期目标是封装一套公司内部用的基础组件和业务组件,目标是快速,试水。本篇记录一个花费时间较长的组件Radio。

使用技术

  • React & React Native
  • useState,useContext,useRef
  • TypeScript
  • Function Component

设计&实现

作为一个主Java后端开发的人来说,设计一个前端的组件往往是参照常见的App。这次的比较简单,只需要找两个图标,见下图:

select 图标
select 图标

为了符合当前主题,改成蓝色即可。

结构

src ├─assets // 资源 │ └─img // 图片 │ radio_button_select.png // 选中 │ radio_button_unselect.png // 非选中 ├─components //组件位置 │ └─radio │ PropsType.tsx // 组件属性 & 接口 │ Radio.tsx // 单个选项 │ RadioContext.tsx // 上下文 │ RadioGroup.tsx // 组

组件属性 & 接口

代码语言:typescript
复制
// Radio的接口
export interface IRadio {
  /** 值 */
  value: string;
  /** 显示的文本 */
  label: string;
  /** 默认被选中 */
  defalutChecked?: boolean;
  /** 改变事件 */
  onChange?: (value: boolean) => void;
}

前面三个都可以理解,最后一个是想到下面一种场景:

调查问卷中,根据不同选项,会有后续不同的问题。此时用来触发其他联动事件。

Radio

设计草图
设计草图

根据草图,想象得到是图片 + 文本的结构。再用可点击的控件包裹,于是得到如下

代码语言:jsx
复制
    <Pressable
      onPress={() => {
        const changedValue = !checked;
        setChecked(() => changedValue);
        context?.onChange(value);
      }}>
      <View style={styles.container}>
        <Image
          style={styles.img}
          source={checked ? selectImgPath : unSelectImgPath}
        />
        <Text style={styles.text}>{label}</Text>
      </View>
    </Pressable>

补充说明:

  • Pressable: 初版使用的是TouchableHighlight,但官方文档中说,未来以Pressable为中心,所以就没再用。该控件是为了模拟点击时候的高亮,并且支持了更细颗粒度的触控效果,见下图:
官方文档摘选
官方文档摘选
  • 第四行的 setChecked(() => changedValue);

需要在内部定义:

代码语言:jsx
复制
const [checked, setChecked] = useState(false);

是用来让画面上的显示和内部属性双向绑定,useState内部的值为初始值,可以是很多类型,甚至函数。

使用格式也比较固定,假设xxx为属性名,是布尔(boolean)类型,那么得到如下:注意大小写

代码语言:jsx
复制
const [xxx, setXxx] = useState(false)

想要修改值的话,就用第setXxx

Q: 这里的值为什么用箭头函数() => {}再包裹一下? A: 有时候需要调用完set方法后,直接拿到修改后的值再去做其他修改。 比如你点击之后,想log一下看看真实的值,会发现一直保留上次的结果,与实际不同步。 这时候需要考虑使用这种方式了。主要问题来自React的渲染机制。

  • 第5行保留,后面说
  • style={styles.xxx} 没有配置统一的主题,都写在各自的控件中。
代码语言:jsx
复制
// 引入的控件
import {Image, Pressable, StyleSheet, Text, View} from 'react-native';
。。。省略
// 图片路径
const selectImgPath = require('../../assets/img/radio_button_select.png');
const unSelectImgPath = require('../../assets/img/radio_button_unselect.png');
。。。省略
const styles = StyleSheet.create({
  container: {
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'center',
  },
  img: {
    height: 20,
    width: 20,
  },
  text: {
    fontSize: 16,
    marginLeft: 10,
  },
});

RadioGroup 和 RadioGroupContext

用普通的View包裹即可,再加上一些样式。

甚至定义都可以写成:

代码语言:jsx
复制
// ViewProps来自原生组件View的接口
const RadioGroup = (props: ViewProps) => {...

到这里,画面就结束了。

但其实控件是“死”的,目前还没有控制住“单选”这一功能,而且外面(父组件)也拿不到我选了什么。

这时候一个Hooks的作用就出现了!那就是useContext

代码语言:jsx
复制
import * as React from 'react';

// 接口定义
interface IRadioGroupContext {
  /** 当前选中的值 */
  currentValue: string;
  /** 更改事件 */
  onChange: (value: string) => void;
}
// 创建上下文
const RadioGroupContext = React.createContext<IRadioGroupContext | null>(
  null,
);
// 导出&起别名
export const RadioGroupContextProvider = THSRadioGroupContext.Provider;
// 导出
export default RadioGroupContext;

RadioGroup中的使用:

代码语言:jsx
复制
  const onChange = (targetValue: string) => {
    setCurrentValue(() => targetValue);
  };

  return (
    <View style={styles.container}>
      <RadioGroupContextProvider
        value={{
          currentValue: currentValue,
          onChange: onChange,
        }}>
        {children}
      </RadioGroupContextProvider>
    </View>
  );

将需要使用上下文的组件包裹起来,然后所有的值放在value中,这样就成了全局共享的变量、方法。

Radio中的第5行就是为了调用父组件的方法。另外补充:

代码语言:jsx
复制
  // 得到上下文
  const context = useContext(RadioGroupContext);

  useEffect(() => {
    setChecked(() => context?.currentValue === value);
  }, [context?.currentValue, value]);

useEffect是组件初始化和再次渲染都会执行的方法,第二个参数是调用了外部的变量就会触发更新。

效果图

效果图
效果图

让外面取到当前的值

使用的是useRef,主要分两步骤

  • 第一:包裹原组件
代码语言:jsx
复制
const RadioGroup = forwardRef((props: IRadioGroup, ref: RefRadio) => {...});
  • 第二:暴漏属性、方法
代码语言:jsx
复制
  useImperativeHandle(ref, () => {
    currentValue;
  });

外面要想取得,就可以这样

代码语言:jsx
复制
const radioRef = React.useRef<any>(null);
。。。
console.log(ref.?current?.currentValue)
。。。
 <RadioGroup ref={radioRef}>。。。

使用文档

正确来说,要引入StoryBook库来展示。可是时间,能力有限,就采用Excle的方式了。

格式是组件名,图例,使用,接口属性。

总结

以上就是一个简单的Radio组件开发流程了。

作为一个后端同学,对于React的开发还是比较好上手的,只是有些时候会比较难理解一些函数钩子(Hooks)。比如踩过无数次坑的useMemo,以至于现在都不怎么考虑使用了。

还有一点需要注意的就是做好规范,搭建项目的时候,把eslint的配置统一。包括eslint react的插件,能帮助我们更安全高效的使用和学习React Native。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 使用技术
  • 设计&实现
    • 结构
      • 组件属性 & 接口
        • Radio
          • RadioGroup 和 RadioGroupContext
            • 效果图
              • 让外面取到当前的值
              • 使用文档
              • 总结
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
              http://www.vxiaotou.com