前端学习踩坑(4)
前端学习踩坑(4)—— 自定义组件
引言
由于一些样式和功能的特殊要求,RN自带的很多组件已经无法满足我的开发要求了,所以我开始了项目RN组件库的初期搭建。
听起来很高大上,但是其实目前我的办法就是在项目里面新建一个组件,然后用接口约束props,在需求和现有组件的基础上自己封装一些组件出来,下面我就挑几个说说。
WCTextInput
首先,我将组件实现功能所需要的props类型抽象为接口来进行约束:
由于设计的需求,实际上输入框分为三种类型:
输入框前面有提示的
密码类型的(多了是否可见的按钮 + 文本不可显示)
最普通的
一开始,我的想法是先使用最基本的View、TextInput、Text为基础封装一个最普通的输入框,并在普通的输入框上继续封装上述的两种较为复杂的输入框。但是在我和亲爱的mentor沟通过后,他给了我一个很好的思路。
其实没有必要封装那么多,是否有前面的提示完全可以自适应渲染。只有判断content不为空字符串的时候才会渲染,否则就直接不渲染那个Text。密码和普通的文字输入,也可以在props中添加一个type来修饰,这样大大减少了组件之间封装和传递的多余代码,非常的Amazing!
代码讲解
1 | content?: string, |
- content(可选参数)
提示信息,渲染在输入框的前方。如果为空字符串或不设置,则将输入框渲染为整个组件的完全大小。
- placeholder(可选参数)
输入框中的提示信息
- keyboardType(可选参数)
键盘类型,默认值为
default
,可选值有:
- focusedViewStyle(可选参数)
设置被聚焦到时的样式,默认样式为设计规范中所规定的
- unfocusedViewStyle(可选参数)
设置不被聚焦到时的样式,默认样式为设计规范中所规定的
- width(可选参数)
设置宽度,默认值为295px
- height(可选参数)
设置高度,默认值为56px
- onChangeText(必要参数)
文本改变时的回调函数
- value(可选参数)
输入框中的初始值
- type(可选参数)
输入框的类型,包括
text为普通文本输入框,password为密码输入框,自带一个用于控制密码可见性的按钮以及文本的可见性功能。
优雅的初始值赋值
1 | let { |
在使用解构赋值的基础上,我从mentor那里学到了一种优雅的初始值赋值方法。
1 | const changeDivStyle = () => { |
样式控制函数,如果传入了一个自定义样式,则应该使用自定义样式,并且返回的样式应该由是否聚焦来控制。
最后根据type进行条件渲染,返回组件即可。
效果展示
- 选中状态下
- 非选中状态下
- 密码可见状态下
WCNavigation
代码讲解
1 | header?: string; |
- header(可选参数)
显示在导航栏上的标题
- navigation(必要参数)
用于导航的navigation参数
- backName(必要参数)
单击左上角返回按钮要返回到的Navigation名称
WCButton
代码讲解
1 | text: string, |
- text(必选参数)
按钮内的文字
- usableViewStyle(可选参数)
按钮可使用时的样式
- unusableViewStyle(可选参数)
按钮不可使用时的样式
- width(可选参数)
按钮宽度,默认值为295px
- height(可选参数)
按钮高度,默认值为64px
- onPress(必选参数)
触发后的回调函数
- usable(按钮可用性)
按钮是否可用,默认值为true(不可用状态下按钮无点击动画且无法触发回调函数)
WC验证码输入组件
这个组件暂时未封装,感觉似乎没有什么复用的价值(毕竟好像只用在登录短信验证码输入),这里只记录一下编写过程
其中有两个核心的难点:
- 跨状态保存短信验证码发送冷却时间
短信验证码冷却发送时间不仅仅要在当前页面内保存,否则只需要退出该页面再返回就可以直接重置验证码cd。
- 特殊的布局要求
由于当前项目正好使用redux来进行了状态管理,所以我们可以将冷却时间和是否冷却作为参数存入redux的loginSlice中管理。并在当前组建中新建两个ref,一个用来保存是否冷却,一个用来保存冷却时间。(是否冷却其实可以由冷却时间推出,但是为了方便后面的条件渲染,所以增加了这么一个变量)。
1 | let login = useSelector(selectLogin); |
使用ref是为了防止在setInterval中由于闭包导致state的无法正常更新。
然后,我们分析能让验证码进入cd的两个条件:
- 进入页面后redux中仍然保存还在冷却中(上一次发送完还未冷却成功)
- 进入页面后冷却时间结束,点击重新发送
为了解决第一个套件,这里我们使用无依赖参数的useEffect来使得组件被挂载时就根据redux中的数据判断是否需要渲染。
1 | useEffect(() => change(), []); |
为了解决第二个条件,我们为“重新发送”按钮编写新的回调函数:
1 | const reloadBtn = () => { |
然后在return中根据ifReload进行条件渲染即可。
为了解决特殊的样式布局需求,最开始我有两种思路:
- 6个框每个都是一个TextInput,使用代码控制他们的焦点移动(即输完一个之后自动跳下一个)
- 利用一个绝对定位的TextInput盖在6个View上面,将TextInput的背景色、文字色,选中色设为透明并将contentWindow设为false,然后利用这个不可见的TextInput中输入的数据来渲染6个View
第一种听起来就很复杂,第二种实际上RN官方给出的原生Api就足够我们实现功能,所以果断第二种。
布局在此不做解释,只是一个简单的absolute。
返回的组件如下:
1 | <TextInput style={Styles.codeInput} selectionColor={'rgba(255,255,255,0)'} contextMenuHidden={true} |
在style中,我们将background-color和color设置为了透明。对于selectionColor,我们使用rgba(255,255,255,0)来设置透明。直接使用transparent来设置在安卓上是完美支持的,但是在ios端选中后仍有灰色的选中背景色,即使使用rgba(0,0,0,0)也不行,查阅资料后发现只能使用rgba(255,255,255,0)。
contextMenuHidden是否隐藏选中文本后上面的选项(比如全选、复制等)。keyboardType表示键盘类型,由于是短信数字验证码,所以直接使用number-pad。
onChangeText中的回调函数主要一个只能输入数字的约束,同时还有如果输入长度超过6,会将value清空从头开始输入(会将第7个输入设为下一次输入的第1个),code使用state保存。
至此,验证码输入组件完成。·