首页 > 解决方案 > React 原生工厂模式

问题描述

我的应用程序中有两种文本输入类型。搜索输入和其他可以有图标的文本输入(用于表单、评论...)。

搜索输入接收一个 onSubmit(用于键盘),它也用于其图标的可触摸不透明度。其他文本输入没有 onSubmit 属性,但它们的图标可以有 onPress 功能。

我从来没有在 React 上实现过工厂模式,我应该使用它吗?如果不是,我应该概括文本输入组件吗?还是可以同时拥有这两个组件?

你怎么看?

搜索输入:

import React, { useState } from "react";
import { View, TouchableOpacity, Keyboard, StyleSheet } from "react-native";
import { TextInput, useTheme } from "react-native-paper";
import { Icon } from "react-native-elements";
import PropTypes from "prop-types";

export default function SearchInput(props) {
  const { colors } = useTheme();

  let { placeholder, maxLength, color, onSearch } = props;

  const [text, setText] = useState("");

  if (!color) {
    color = colors.white;
  }

  const handleChangeText = (text) => {
    setText(text);
  };

  const handleSubmit = () => {
    onSearch(text);
    // Close the keyboard
    Keyboard.dismiss();
  };

  return (
    <>
      <View style={styles.inputContainer}>
        <TextInput
          placeholder={placeholder}
          value={text}
          maxLength={maxLength}
          dense
          autoCorrect={false}
          autoCapitalize="none"
          selectionColor={colors.primary}
          onChangeText={handleChangeText}
          onSubmitEditing={handleSubmit}
          style={[styles.input, { backgroundColor: color }]}
        />
        <View pointerEvents="box-none" style={styles.iconContainer}>
          <TouchableOpacity activeOpacity={0.5} onPress={handleSubmit}>
            <Icon name="search" type="material" color={colors.gray} size={20} />
          </TouchableOpacity>
        </View>
      </View>
    </>
  );
}

SearchInput.propTypes = {
  placeholder: PropTypes.string,
  maxLength: PropTypes.number,
  color: PropTypes.string,
  onSearch: PropTypes.func.isRequired,
};

SearchInput.defaultProps = {
  maxLength: 30,
};

const styles = StyleSheet.create({
  inputContainer: {
    width: "100%",
  },
  input: {
    paddingRight: 5,
    fontSize: 14,
    padding: 5,
  },
  iconContainer: {
    ...StyleSheet.absoluteFillObject,
    justifyContent: "center",
    alignItems: "flex-end",
    marginRight: 5,
  },
});

其他文本输入:

import React, { useState, forwardRef } from "react";
import { View, Text, TouchableOpacity, StyleSheet } from "react-native";
import { TextInput as RNPTextInput, useTheme } from "react-native-paper";
import { Icon } from "react-native-elements";
import PropTypes from "prop-types";

const TextInput = forwardRef((props, ref) => {
  const { colors } = useTheme();

  let {
    placeholder,
    multiline,
    maxLength,
    color,
    icon,
    counter,
    onSubmit,
  } = props;

  const [text, setText] = useState("");

  if (!color) {
    color = colors.white;
  }

  // Default props for optional nested object
  if (icon) {
    if (!icon.color) {
      icon.color = colors.gray;
    }

    if (!icon.size) {
      icon.size = 20;
    }

    if (!icon.onPress) {
      icon.onPress = () => {};
    }
  }

  const handleChangeText = (text) => {
    setText(text);
  };

  const handleIconPress = () => {
    const { onPress } = icon;
    onPress();
  };

  const handleSubmit = () => {
    onSubmit(text);
  };

  return (
    <>
      <View style={styles.inputContainer}>
        <RNPTextInput
          ref={ref}
          placeholder={placeholder}
          value={text}
          multiline={multiline}
          maxLength={maxLength}
          maxHeight={85}
          dense
          autoCorrect={false}
          autoCapitalize="none"
          selectionColor={colors.primary}
          onChangeText={handleChangeText}
          style={[styles.input, { backgroundColor: color }]}
          onSubmitEditing={handleSubmit}
        />
        {icon && (
          <View pointerEvents="box-none" style={styles.iconContainer}>
            <TouchableOpacity activeOpacity={0.5} onPress={handleIconPress}>
              <Icon
                name={icon.name}
                type={icon.type}
                color={icon.color}
                size={icon.size}
              />
            </TouchableOpacity>
          </View>
        )}
      </View>
      {counter && (
        <View style={styles.charactersCounterContainer}>
          <Text style={[styles.charactersCounter, { color: colors.gray }]}>
            {`${text.length}/${maxLength}`}
          </Text>
        </View>
      )}
    </>
  );
});

TextInput.propTypes = {
  placeholder: PropTypes.string,
  multiline: PropTypes.bool,
  maxLength: PropTypes.number,
  color: PropTypes.string,
  icon: PropTypes.shape({
    name: PropTypes.string.isRequired,
    type: PropTypes.string.isRequired,
    color: PropTypes.string,
    size: PropTypes.number,
    onPress: PropTypes.func,
  }),
  counter: PropTypes.bool,
  onSubmit: PropTypes.func,
};

TextInput.defaultProps = {
  placeholder: null,
  multiline: false,
  maxLength: 300,
  icon: null,
  counter: false,
  onSubmit: () => {},
};

export default TextInput;

const styles = StyleSheet.create({
  inputContainer: {
    width: "100%",
  },
  input: {
    paddingRight: 5,
    fontSize: 14,
    padding: 5,
  },
  iconContainer: {
    ...StyleSheet.absoluteFillObject,
    justifyContent: "center",
    alignItems: "flex-end",
    marginRight: 5,
  },
  charactersCounterContainer: {
    marginTop: 5,
    width: "100%",
  },
  charactersCounter: {
    textAlign: "right",
    fontSize: 12,
  },
});

标签: javascriptreactjsreact-nativeexpo

解决方案


推荐阅读