怎么让TextInput获取或者失去焦点?focus()和blur()现在不支持吗?



  • 单看代码没发现问题,如果那些if else的流程符合预期的话
    打印结果呢?



  • @晴明 我打印this.search就是undefined的 我找不到原因



  • @晴明 我是想通过focus()和blur()来实现输入框是否获取焦点 但是我在这里就是获取不到ref
    在我项目的其他地方我是可以通过this.refs获取到的 但是这里不行
    对了 出问题的代码是依赖包里面的文件 我在改react-native-gifted-chat这个包的源代码 出现这个问题是因为我没有把它放在项目目录下吗?



  • undefined那就是ref没有执行咯,所以我说你那些if else的流程确定正确吗?这些基本逻辑都应该断点调试啊,方法是否按期望的顺序执行甚至是否有执行,是否按期望的输入值输入,期望的返回值返回等等,所有的调试都是这样啊。你文件放在哪无所谓啊(规不规范另说),反正最终都是揉到一起在内存里执行



  • @晴明 这些if else是正确的 因为我的视图是正确渲染了 所有其他逻辑都没有问题 就是这里的ref获取不到



  • @晴明 textInput所在的组件的父组件的代码是这样的:

    /* eslint
        no-param-reassign: 0,
        no-use-before-define: ["error", { "variables": false }],
        no-return-assign: 0,
        no-mixed-operators: 0,
        react/sort-comp: 0
    */
    
    import PropTypes from 'prop-types';
    import React from 'react';
    import { Animated, Platform, StyleSheet, View } from 'react-native';
    
    import ActionSheet from '@expo/react-native-action-sheet';
    import moment from 'moment';
    import uuid from 'uuid';
    
    import * as utils from './utils';
    import Actions from './Actions';
    import Avatar from './Avatar';
    import Bubble from './Bubble';
    import SystemMessage from './SystemMessage';
    import MessageImage from './MessageImage';
    import MessageText from './MessageText';
    import Composer from './Composer';
    import Day from './Day';
    import InputToolbar from './InputToolbar';
    import LoadEarlier from './LoadEarlier';
    import Message from './Message';
    import MessageContainer from './MessageContainer';
    import Send from './Send';
    import Time from './Time';
    import GiftedAvatar from './GiftedAvatar';
    
    import {
      MIN_COMPOSER_HEIGHT,
      MAX_COMPOSER_HEIGHT,
      DEFAULT_PLACEHOLDER,
      TIME_FORMAT,
      DATE_FORMAT,
    } from './Constant';
    
    global.inputRef = null;
    moment.locale('zh-cn', {
      months: '一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月'.split('_'),
      monthsShort: '1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月'.split('_'),
      weekdays: '星期日_星期一_星期二_星期三_星期四_星期五_星期六'.split('_'),
      weekdaysShort: '周日_周一_周二_周三_周四_周五_周六'.split('_'),
      weekdaysMin: '日_一_二_三_四_五_六'.split('_'),
      longDateFormat: {
        LT: 'Ah点mm分',
        LTS: 'Ah点m分s秒',
        L: 'YYYY-MM-DD',
        LL: 'YYYY年MMMD日',
        LLL: 'YYYY年MMMD日Ah点mm分',
        LLLL: 'YYYY年MMMD日ddddAh点mm分',
        l: 'YYYY-MM-DD',
        ll: 'YYYY年MMMD日',
        lll: 'YYYY年MMMD日Ah点mm分',
        llll: 'YYYY年MMMD日ddddAh点mm分'
      },
      meridiemParse: /凌晨|早上|上午|中午|下午|晚上/,
      meridiemHour: function (h, meridiem) {
        let hour = h;
        if (hour === 12) {
          hour = 0;
        }
        if (meridiem === '凌晨' || meridiem === '早上' ||
          meridiem === '上午') {
          return hour;
        } else if (meridiem === '下午' || meridiem === '晚上') {
          return hour + 12;
        } else {
          // '中午'
          return hour >= 11 ? hour : hour + 12;
        }
      },
      meridiem: function (hour, minute, isLower) {
        const hm = hour * 100 + minute;
        if (hm < 600) {
          return '凌晨';
        } else if (hm < 900) {
          return '早上';
        } else if (hm < 1130) {
          return '上午';
        } else if (hm < 1230) {
          return '中午';
        } else if (hm < 1800) {
          return '下午';
        } else {
          return '晚上';
        }
      },
      calendar: {
        sameDay: function () {
          return this.minutes() === 0 ? '[今天]Ah[点整]' : '[今天]LT';
        },
        nextDay: function () {
          return this.minutes() === 0 ? '[明天]Ah[点整]' : '[明天]LT';
        },
        lastDay: function () {
          return this.minutes() === 0 ? '[昨天]Ah[点整]' : '[昨天]LT';
        },
        nextWeek: function () {
          let startOfWeek, prefix;
          startOfWeek = moment().startOf('week');
          prefix = this.diff(startOfWeek, 'days') >= 7 ? '[下]' : '[本]';
          return this.minutes() === 0 ? prefix + 'dddAh点整' : prefix + 'dddAh点mm';
        },
        lastWeek: function () {
          let startOfWeek, prefix;
          startOfWeek = moment().startOf('week');
          prefix = this.unix() < startOfWeek.unix() ? '[上]' : '[本]';
          return this.minutes() === 0 ? prefix + 'dddAh点整' : prefix + 'dddAh点mm';
        },
        sameElse: 'LL'
      },
      ordinalParse: /\d{1,2}(日|月|周)/,
      ordinal: function (number, period) {
        switch (period) {
          case 'd':
          case 'D':
          case 'DDD':
            return number + '日';
          case 'M':
            return number + '月';
          case 'w':
          case 'W':
            return number + '周';
          default:
            return number;
        }
      },
      relativeTime: {
        future: '%s内',
        past: '%s前',
        s: '几秒',
        m: '1 分钟',
        mm: '%d 分钟',
        h: '1 小时',
        hh: '%d 小时',
        d: '1 天',
        dd: '%d 天',
        M: '1 个月',
        MM: '%d 个月',
        y: '1 年',
        yy: '%d 年'
      },
      week: {
        // GB/T 7408-1994《数据元和交换格式·信息交换·日期和时间表示法》与ISO 8601:1988等效
        dow: 1, // Monday is the first day of the week.
        doy: 4  // The week that contains Jan 4th is the first week of the year.
      }
    });
    
    class GiftedChat extends React.Component {
    
      constructor(props) {
        super(props);
    
        // default values
        this._isMounted = false;
        this._keyboardHeight = 0;
        this._bottomOffset = 0;
        this._maxHeight = null;
        this._isFirstLayout = true;
        this._locale = 'en';
        this._messages = [];
    
        this.state = {
          isInitialized: false, // initialization will calculate maxHeight before rendering the chat
          composerHeight: MIN_COMPOSER_HEIGHT,
          messagesContainerHeight: null,
          typingDisabled: false,
        };
    
        this.onKeyboardWillShow = this.onKeyboardWillShow.bind(this);
        this.onKeyboardWillHide = this.onKeyboardWillHide.bind(this);
        this.onKeyboardDidShow = this.onKeyboardDidShow.bind(this);
        this.onKeyboardDidHide = this.onKeyboardDidHide.bind(this);
        this.onSend = this.onSend.bind(this);
        this.getLocale = this.getLocale.bind(this);
        this.onInputSizeChanged = this.onInputSizeChanged.bind(this);
        this.onInputTextChanged = this.onInputTextChanged.bind(this);
        this.onMainViewLayout = this.onMainViewLayout.bind(this);
        this.onInitialLayoutViewLayout = this.onInitialLayoutViewLayout.bind(this);
    
        this.invertibleScrollViewProps = {
          inverted: this.props.inverted,
          keyboardShouldPersistTaps: this.props.keyboardShouldPersistTaps,
          onKeyboardWillShow: this.onKeyboardWillShow,
          onKeyboardWillHide: this.onKeyboardWillHide,
          onKeyboardDidShow: this.onKeyboardDidShow,
          onKeyboardDidHide: this.onKeyboardDidHide,
        };
      }
    
      static append(currentMessages = [], messages, inverted = true) {
        if (!Array.isArray(messages)) {
          messages = [messages];
        }
        return inverted ? messages.concat(currentMessages) : currentMessages.concat(messages);
      }
    
      static prepend(currentMessages = [], messages, inverted = true) {
        if (!Array.isArray(messages)) {
          messages = [messages];
        }
        return inverted ? currentMessages.concat(messages) : messages.concat(currentMessages);
      }
    
      getChildContext() {
        return {
          actionSheet: () => this._actionSheetRef,
          getLocale: this.getLocale,
        };
      }
    
      componentWillMount() {
        const { messages, text } = this.props;
        this.setIsMounted(true);
        this.initLocale();
        this.setMessages(messages || []);
        this.setTextFromProp(text);
      }
    
      componentWillUnmount() {
        this.setIsMounted(false);
      }
    
      componentWillReceiveProps(nextProps = {}) {
        const { messages, text } = nextProps;
        this.setMessages(messages || []);
        this.setTextFromProp(text);
      }
    
      initLocale() {
        if (this.props.locale === null || moment.locales().indexOf(this.props.locale) === -1) {
          this.setLocale('en');
        } else {
          this.setLocale(this.props.locale);
        }
      }
    
      setLocale(locale) {
        this._locale = locale;
      }
    
      getLocale() {
        return this._locale;
      }
    
      setTextFromProp(textProp) {
        // Text prop takes precedence over state.
        if (textProp !== undefined && textProp !== this.state.text) {
          this.setState({ text: textProp });
        }
      }
    
      getTextFromProp(fallback) {
        if (this.props.text === undefined) {
          return fallback;
        }
        return this.props.text;
      }
    
      setMessages(messages) {
        this._messages = messages;
      }
    
      getMessages() {
        return this._messages;
      }
    
      setMaxHeight(height) {
        this._maxHeight = height;
      }
    
      getMaxHeight() {
        return this._maxHeight;
      }
    
      setKeyboardHeight(height) {
        this._keyboardHeight = height;
      }
    
      getKeyboardHeight() {
        if (Platform.OS === 'android' && !this.props.forceGetKeyboardHeight) {
          // For android: on-screen keyboard resized main container and has own height.
          // @see https://developer.android.com/training/keyboard-input/visibility.html
          // So for calculate the messages container height ignore keyboard height.
          return 0;
        }
        return this._keyboardHeight;
      }
    
    
      setBottomOffset(value) {
        this._bottomOffset = value;
      }
    
      getBottomOffset() {
        return this._bottomOffset;
      }
    
      setIsFirstLayout(value) {
        this._isFirstLayout = value;
      }
    
      getIsFirstLayout() {
        return this._isFirstLayout;
      }
    
      setIsTypingDisabled(value) {
        this.setState({
          typingDisabled: value,
        });
      }
    
      getIsTypingDisabled() {
        return this.state.typingDisabled;
      }
    
      setIsMounted(value) {
        this._isMounted = value;
      }
    
      getIsMounted() {
        return this._isMounted;
      }
    
      // TODO: setMinInputToolbarHeight
      getMinInputToolbarHeight() {
        return this.props.renderAccessory
          ? this.props.minInputToolbarHeight * 2
          : this.props.minInputToolbarHeight;
      }
      calculateInputToolbarHeight(composerHeight) {
        return composerHeight + (this.getMinInputToolbarHeight() - MIN_COMPOSER_HEIGHT);
      }
    
      /**
       * Returns the height, based on current window size, without taking the keyboard into account.
       */
      getBasicMessagesContainerHeight(composerHeight = this.state.composerHeight) {
        return this.getMaxHeight() - this.calculateInputToolbarHeight(composerHeight);
      }
    
      /**
       * Returns the height, based on current window size, taking the keyboard into account.
       */
      getMessagesContainerHeightWithKeyboard(composerHeight = this.state.composerHeight) {
        return this.getBasicMessagesContainerHeight(composerHeight) - this.getKeyboardHeight() + this.getBottomOffset();
      }
    
      prepareMessagesContainerHeight(value) {
        if (this.props.isAnimated === true) {
          return new Animated.Value(value);
        }
        return value;
      }
    
      onKeyboardWillShow(e) {
        this.setIsTypingDisabled(true);
        this.setKeyboardHeight(e.endCoordinates ? e.endCoordinates.height : e.end.height);
        this.setBottomOffset(this.props.bottomOffset);
        const newMessagesContainerHeight = this.getMessagesContainerHeightWithKeyboard();
        if (this.props.isAnimated === true) {
          Animated.timing(this.state.messagesContainerHeight, {
            toValue: newMessagesContainerHeight,
            duration: 210,
          }).start();
        } else {
          this.setState({
            messagesContainerHeight: newMessagesContainerHeight,
          });
        }
      }
    
      onKeyboardWillHide() {
        this.setIsTypingDisabled(true);
        this.setKeyboardHeight(0);
        this.setBottomOffset(0);
        const newMessagesContainerHeight = this.getBasicMessagesContainerHeight();
        if (this.props.isAnimated === true) {
          Animated.timing(this.state.messagesContainerHeight, {
            toValue: newMessagesContainerHeight,
            duration: 210,
          }).start();
        } else {
          this.setState({
            messagesContainerHeight: newMessagesContainerHeight,
          });
        }
      }
    
      onKeyboardDidShow(e) {
        if (Platform.OS === 'android') {
          this.onKeyboardWillShow(e);
        }
        this.setIsTypingDisabled(false);
      }
    
      onKeyboardDidHide(e) {
        if (Platform.OS === 'android') {
          this.onKeyboardWillHide(e);
        }
        this.setIsTypingDisabled(false);
      }
    
      scrollToBottom(animated = true) {
        if (this._messageContainerRef === null) {
          return;
        }
        this._messageContainerRef.scrollTo({ y: 0, animated });
      }
    
    
      renderMessages() {
        const AnimatedView = this.props.isAnimated === true ? Animated.View : View;
        return (
          <AnimatedView
            style={{
              height: this.state.messagesContainerHeight,
            }}
          >
            <MessageContainer
              {...this.props}
              invertibleScrollViewProps={this.invertibleScrollViewProps}
              messages={this.getMessages()}
              ref={(component) => (this._messageContainerRef = component)}
    
            />
            {this.renderChatFooter()}
          </AnimatedView>
        );
      }
    
      onSend(messages = [], shouldResetInputToolbar = false) {
        if (!Array.isArray(messages)) {
          messages = [messages];
        }
        messages = messages.map((message) => {
          return {
            ...message,
            user: this.props.user,
            createdAt: new Date(),
            _id: this.props.messageIdGenerator(),
          };
        });
    
        if (shouldResetInputToolbar === true) {
          this.setIsTypingDisabled(true);
          this.resetInputToolbar();
        }
    
        this.props.onSend(messages);
        this.scrollToBottom();
    
        if (shouldResetInputToolbar === true) {
          setTimeout(() => {
            if (this.getIsMounted() === true) {
              this.setIsTypingDisabled(false);
            }
          }, 100);
        }
      }
    
      resetInputToolbar() {
        if (this.textInput) {
          this.textInput.clear();
        }
        this.notifyInputTextReset();
        const newComposerHeight = MIN_COMPOSER_HEIGHT;
        const newMessagesContainerHeight = this.getMessagesContainerHeightWithKeyboard(newComposerHeight);
        this.setState({
          text: this.getTextFromProp(''),
          composerHeight: newComposerHeight,
          messagesContainerHeight: this.prepareMessagesContainerHeight(newMessagesContainerHeight),
        });
      }
    
      focusTextInput() {
        if (this.textInput) {
          this.textInput.focus();
        }
      }
    
      onInputSizeChanged(size) {
        const newComposerHeight = Math.max(MIN_COMPOSER_HEIGHT, Math.min(MAX_COMPOSER_HEIGHT, size.height));
        const newMessagesContainerHeight = this.getMessagesContainerHeightWithKeyboard(newComposerHeight);
        this.setState({
          composerHeight: newComposerHeight,
          messagesContainerHeight: this.prepareMessagesContainerHeight(newMessagesContainerHeight),
        });
      }
    
      onInputTextChanged(text) {
        if (this.getIsTypingDisabled()) {
          return;
        }
        if (this.props.onInputTextChanged) {
          this.props.onInputTextChanged(text);
        }
        // Only set state if it's not being overridden by a prop.
        if (this.props.text === undefined) {
          this.setState({ text });
        }
      }
    
      notifyInputTextReset() {
        if (this.props.onInputTextChanged) {
          this.props.onInputTextChanged('');
        }
      }
    
      onInitialLayoutViewLayout(e) {
        const { layout } = e.nativeEvent;
        if (layout.height <= 0) {
          return;
        }
        this.notifyInputTextReset();
        this.setMaxHeight(layout.height);
        const newComposerHeight = MIN_COMPOSER_HEIGHT;
        const newMessagesContainerHeight = this.getMessagesContainerHeightWithKeyboard(newComposerHeight);
        this.setState({
          isInitialized: true,
          text: this.getTextFromProp(''),
          composerHeight: newComposerHeight,
          messagesContainerHeight: this.prepareMessagesContainerHeight(newMessagesContainerHeight),
        });
      }
    
      onMainViewLayout(e) {
        // fix an issue when keyboard is dismissing during the initialization
        const { layout } = e.nativeEvent;
        if (this.getMaxHeight() !== layout.height || this.getIsFirstLayout() === true) {
          this.setMaxHeight(layout.height);
          this.setState({
            messagesContainerHeight: this.prepareMessagesContainerHeight(this.getBasicMessagesContainerHeight()),
          });
        }
        if (this.getIsFirstLayout() === true) {
          this.setIsFirstLayout(false);
        }
      }
    
      renderInputToolbar() {
        const inputToolbarProps = {
          ...this.props,
          text: this.getTextFromProp(this.state.text),
          composerHeight: Math.max(MIN_COMPOSER_HEIGHT, this.state.composerHeight),
          onSend: this.onSend,
          onInputSizeChanged: this.onInputSizeChanged,
          onTextChanged: this.onInputTextChanged,
          textInputProps: {
            ...this.props.textInputProps,
            ref: (textInput) => (this.textInput = textInput),
            maxLength: this.getIsTypingDisabled() ? 0 : this.props.maxInputLength,
          },
        };
        if (this.props.renderInputToolbar) {
          return this.props.renderInputToolbar(inputToolbarProps);
        }
        return (
          <InputToolbar
            {...inputToolbarProps}
          />
        );
      }
    
      renderChatFooter() {
        if (this.props.renderChatFooter) {
          const footerProps = {
            ...this.props,
          };
          return this.props.renderChatFooter(footerProps);
        }
        return null;
      }
    
      renderLoading() {
        if (this.props.renderLoading) {
          return this.props.renderLoading();
        }
        return null;
      }
    
      render() {
        if (this.state.isInitialized === true) {
          return (
            <ActionSheet ref={(component) => (this._actionSheetRef = component)}>
              <View style={styles.container} onLayout={this.onMainViewLayout}>
                {this.renderMessages()}
                {this.renderInputToolbar()}
              </View>
            </ActionSheet>
          );
        }
        return (
          <View style={styles.container} onLayout={this.onInitialLayoutViewLayout}>
            {this.renderLoading()}
          </View>
        );
      }
    
    }
    
    const styles = StyleSheet.create({
      container: {
        flex: 1,
      },
    });
    
    GiftedChat.childContextTypes = {
      actionSheet: PropTypes.func,
      getLocale: PropTypes.func,
    };
    
    GiftedChat.defaultProps = {
      messages: [],
      text: undefined,
      placeholder: DEFAULT_PLACEHOLDER,
      messageIdGenerator: () => uuid.v4(),
      user: {},
      onSend: () => { },
      locale: null,
      timeFormat: TIME_FORMAT,
      dateFormat: DATE_FORMAT,
      isAnimated: Platform.select({
        ios: true,
        android: false,
      }),
      loadEarlier: false,
      onLoadEarlier: () => { },
      isLoadingEarlier: false,
      renderLoading: null,
      renderLoadEarlier: null,
      renderAvatar: undefined,
      showUserAvatar: false,
      onPressAvatar: null,
      renderAvatarOnTop: false,
      renderBubble: null,
      renderSystemMessage: null,
      onLongPress: null,
      renderMessage: null,
      renderMessageText: null,
      renderMessageImage: null,
      imageProps: {},
      lightboxProps: {},
      textInputProps: {},
      listViewProps: {},
      renderCustomView: null,
      renderDay: null,
      renderTime: null,
      renderFooter: null,
      renderChatFooter: null,
      renderInputToolbar: null,
      renderComposer: null,
      renderActions: null,
      renderSend: null,
      renderAccessory: null,
      onPressActionButton: null,
      bottomOffset: 0,
      minInputToolbarHeight: 44,
      keyboardShouldPersistTaps: Platform.select({
        ios: 'never',
        android: 'always',
      }),
      onInputTextChanged: null,
      maxInputLength: null,
      forceGetKeyboardHeight: false,
      inverted: true,
    };
    
    GiftedChat.propTypes = {
      messages: PropTypes.arrayOf(PropTypes.object),
      text: PropTypes.string,
      placeholder: PropTypes.string,
      messageIdGenerator: PropTypes.func,
      user: PropTypes.object,
      onSend: PropTypes.func,
      locale: PropTypes.string,
      timeFormat: PropTypes.string,
      dateFormat: PropTypes.string,
      isAnimated: PropTypes.bool,
      loadEarlier: PropTypes.bool,
      onLoadEarlier: PropTypes.func,
      isLoadingEarlier: PropTypes.bool,
      renderLoading: PropTypes.func,
      renderLoadEarlier: PropTypes.func,
      renderAvatar: PropTypes.func,
      showUserAvatar: PropTypes.bool,
      onPressAvatar: PropTypes.func,
      renderAvatarOnTop: PropTypes.bool,
      renderBubble: PropTypes.func,
      renderSystemMessage: PropTypes.func,
      onLongPress: PropTypes.func,
      renderMessage: PropTypes.func,
      renderMessageText: PropTypes.func,
      renderMessageImage: PropTypes.func,
      imageProps: PropTypes.object,
      lightboxProps: PropTypes.object,
      renderCustomView: PropTypes.func,
      renderDay: PropTypes.func,
      renderTime: PropTypes.func,
      renderFooter: PropTypes.func,
      renderChatFooter: PropTypes.func,
      renderInputToolbar: PropTypes.func,
      renderComposer: PropTypes.func,
      renderActions: PropTypes.func,
      renderSend: PropTypes.func,
      renderAccessory: PropTypes.func,
      onPressActionButton: PropTypes.func,
      bottomOffset: PropTypes.number,
      minInputToolbarHeight: PropTypes.number,
      listViewProps: PropTypes.object,
      keyboardShouldPersistTaps: PropTypes.oneOf(['always', 'never', 'handled']),
      onInputTextChanged: PropTypes.func,
      maxInputLength: PropTypes.number,
      forceGetKeyboardHeight: PropTypes.bool,
      inverted: PropTypes.bool,
      textInputProps: PropTypes.object,
    };
    
    export {
      GiftedChat,
      Actions,
      Avatar,
      Bubble,
      SystemMessage,
      MessageImage,
      MessageText,
      Composer,
      Day,
      InputToolbar,
      LoadEarlier,
      Message,
      MessageContainer,
      Send,
      Time,
      GiftedAvatar,
      utils,
    };
    


  • @1111mp 在render中ActionSheet有一个ref 这个会影响textInput的ref吗?
    0_1539167502838_7625f0a5-2da0-4aed-8c00-a632d1886745-image.png



  • 你看死的代码看不出其值的变化,
    你要做的就是在ref中打上断点,在onpress里打上断点,在各个可疑的地方打上断点,然后观察this的值,this.search的值,直到发现在哪个地方不对,再反过来思考哪里的流程不对



  • @晴明 嗯 我刚刚在ref中加了调试 发现这个方法没有执行 现在在找没有执行ref的问题



  • @1111mp 其他地方的打印和调试代码都有执行



  • @晴明 0_1539168707899_4a1bef37-ef54-4bc7-852b-de895bb237f7-image.png
    这里的代码我有点看不懂 这里的ref应该是textInput的ref吧?是不是这里的问题导致?



  • @晴明 你好 我已经解决了这个问题 就是我最后贴出来的代码造成的 ref在父组件中已经传递并执行了 所以我不需要在子组件中使用ref 在textInput的父组件中是可以获取到的 真的非常感谢你 谢谢啊



  • 不要轻易用{...this.props}这种笼统的写法(不可控,你根本不知道哪些属性重叠),你的问题就是{...this.props.textInputProps}里的ref覆盖了单独写的ref



  • @晴明 好的 谢谢 我会注意的