前言
我在使用native-base的checkbox组件时,发现一个问题,在issues查看说需要降版本还是存在同样的问题,
所以打算自己实现一个checkboxgroup组件
使用@react-stately来实现
react-stately是一个React Hooks库,为许多常见的组件提供状态管理,核心逻辑,并且是跨平台的
@react-stately和@react-native-aria在native-base库中有引入,不需要yarn安装,重写checkbox 逻辑就可以了
// Checkbox.js
import React, { useContext, useRef } from "react";
import { Pressable, View, Text } from "react-native";
import { useCheckbox, useCheckboxGroupItem } from "@react-native-aria/checkbox";
import { useToggleState } from "@react-stately/toggle";
import MaterialCommunityIcons from "react-native-vector-icons/MaterialCommunityIcons";
import { CheckboxGroupContext } from "./CheckboxGroup";
// 多选
export function Checkbox(props) {
let groupState = useContext(CheckboxGroupContext);
let inputRef = useRef(null);
let { inputProps } = groupState
? useCheckboxGroupItem(
{
...props,
isRequired: props.isRequired,
validationState: props.validationState,
},
groupState,
inputRef
)
: useCheckbox(props, useToggleState(props), inputRef);
let icon = inputProps.checked ? "checkbox-marked" : "checkbox-blank-outline";
const iconColor = props.isDisabled ? "#d1d1d1" : "#3b82f6";
return (
<Pressable {...inputProps}>
<View style={{ flexDirection: "row", alignItems: "center" }}>
<MaterialCommunityIcons size={30} color={iconColor} name={icon} />
<Text>{props.children}</Text>
</View>
</Pressable>
);
}
// CheckboxGroup.js
import React from "react";
import { useCheckboxGroupState } from "@react-stately/checkbox";
import { useCheckboxGroup } from "@react-native-aria/checkbox";
import { Text, View } from "react-native";
export let CheckboxGroupContext = React.createContext(null);
export function CheckboxGroup(props) {
let { children, label } = props;
let state = useCheckboxGroupState(props);
let { groupProps, labelProps } = useCheckboxGroup(props, state);
return (
<View {...groupProps}>
{label && <Text {...labelProps}>{label}</Text>}
<CheckboxGroupContext.Provider value={state}>
{children}
</CheckboxGroupContext.Provider>
</View>
);
}
调用组件App.js
import React from "react";
import { CheckboxGroup } from "@/components/Checkbox/CheckboxGroup";
import { Checkbox } from "@/components/Checkbox/Checkbox";
import { Heading, HStack, VStack, Text, Box, View } from "native-base";
const Example = () => {
const [groupValue, setGroupValue] = React.useState(["1"]);
return (
<Box alignItems="center">
<VStack space={2}>
<HStack alignItems="baseline">
<Heading fontSize="lg">Hobbies</Heading>
</HStack>
<VStack>
<Box>
<Text>选择: ({groupValue.length})</Text>
</Box>
</VStack>
<CheckboxGroup
value={groupValue}
accessibilityLabel="pick an item"
onChange={setGroupValue}
>
<Checkbox value="1" isSelected>
1
</Checkbox>
<Checkbox value="2" isDisabled>
2
</Checkbox>
<Checkbox value="3">3</Checkbox>
<Checkbox value="4">4</Checkbox>
</CheckboxGroup>
</VStack>
</Box>
);
};
export default Example;
自定义checkbox
- @/hooks/toggle/index.js 设置toggle 状态
import React from 'react'
export function useToggleState(props) {
let { isReadOnly } = props;
let [isSelected, setSelected] = React.useState(props.isSelected || false);
function updateSelected(value) {
if (!isReadOnly) {
setSelected(value);
}
}
function toggleState() {
if (!isReadOnly) {
setSelected(!isSelected);
}
}
return {
isSelected,
setSelected: updateSelected,
toggle: toggleState,
};
}
- checkbox.js 创建Checkbox组件
import React, { useContext, useRef } from "react";
import { Pressable, View, Text } from "react-native";
import { useToggleState } from "@/hooks/toggle";
import MaterialCommunityIcons from "react-native-vector-icons/MaterialCommunityIcons";
export function Checkbox(props) {
let {isSelected, toggle} = useToggleState(props)
let icon = isSelected ? "checkbox-marked" : "checkbox-blank-outline";
const iconColor = props.isDisabled ? "#d1d1d1" : "#3b82f6";
return (
<Pressable onPress={toggle}>
<View style={{ flexDirection: "row", alignItems: "center" }}>
<MaterialCommunityIcons size={30} color={iconColor} name={icon} />
<Text>{props.children}</Text>
</View>
</Pressable>
);
}
- 调用,单个Checkbox可以切换状态
<Checkbox value="3">3</Checkbox>
- 加上checkboxGroup之后,简单实现一下
import React from "react";
import { View,Pressable, VStack, Text, Box } from "native-base";
import MaterialCommunityIcons from "react-native-vector-icons/MaterialCommunityIcons";
let CheckboxGroupContext = React.createContext(null);
function useCheckboxGroupState(props) {
const {onChange, value} = props
const [selectedValues, setselectedValues] = React.useState(value|| [])
const result = {
value: selectedValues,
isSelected(value) {
return selectedValues.includes(value);
},
toggleValue(value) {
let cv = selectedValues.includes(value)
? selectedValues.filter(existingValue => existingValue !== value)
: selectedValues.concat(value)
setselectedValues(cv);
onChange(cv)
}
}
return result
}
function useCheckboxGroupItem(props, groupState) {
const {value, isDisabled} = props
const isSelected = groupState.isSelected(value)
return {
onPress: (e) => {
if (isDisabled) return
groupState.toggleValue(value)
if (props.onChange) {
props.onChange(isSelected);
}
},
checked: isSelected,
}
}
function useCheckbox(props) {
const {value, isDisabled} = props
const [isSelected,setIsSelected] = React.useState(props.isSelected || false)
return {
onPress: (e) => {
if (isDisabled) return
setIsSelected(!isSelected)
if (props.onChange) {
props.onChange(isSelected);
}
},
checked: isSelected,
}
}
function CheckboxGroup(props) {
let { children } = props;
let state = useCheckboxGroupState(props);
return (
<CheckboxGroupContext.Provider value={state}>
{children}
</CheckboxGroupContext.Provider>
);
}
function Checkbox(props) {
const {isDisabled, children} = props
let groupState = React.useContext(CheckboxGroupContext);
let inputRef = React.useRef(null);
let inputProps= groupState
? useCheckboxGroupItem(props, groupState, inputRef)
: useCheckbox(props)
let icon = inputProps.checked ? "checkbox-marked" : "checkbox-blank-outline";
const iconColor = isDisabled ? "#d1d1d1" : "#3b82f6";
return (
<Pressable {...inputProps}>
<View style={{ flexDirection: "row", alignItems: "center" }}>
<MaterialCommunityIcons size={30} color={iconColor} name={icon} />
<Text>{children}</Text>
</View>
</Pressable>
)
}
export default function TestCheckBox() {
const [groupValue, setGroupValue] = React.useState(["1"]);
return (
<Box>
<VStack>
<Box>
<Text>选择: ({groupValue.length},{groupValue})</Text>
</Box>
</VStack>
<CheckboxGroup
value={groupValue}
onChange={setGroupValue}
>
<Checkbox value="1" isSelected>
1
</Checkbox>
<Checkbox value="2" isDisabled>
2
</Checkbox>
<Checkbox value="3">3</Checkbox>
<Checkbox value="4">4</Checkbox>
</CheckboxGroup>
</Box>
)
}