在useState之后使用eventListener keyup突变引用来持久保存值的问题

时间:2020-07-15 20:32:57

标签: javascript reactjs typescript react-hooks

我已经在这个问题上停留了2天,现在不知道该怎么办...

我有一个自动制表符输入,重点放在keyUp事件的下一个输入元素上。我的挑战是,在填充所有6个输入之后,我需要将输入值存储在状态中,即使用useState。问题是,当我调用函数存储数据时,输入内部的值不会保留。

面临的挑战主要是因为当我继续编辑我的值时,我一直在增加值数组totpCodeValues,我已经尝试检查长度以确保不会创建扩展所需长度的元素。 (长度= 6),如果我已经填充了所有值并正在编辑其中的一些值,那么我会寻找索引来使数组变异。该问题无法正常运行,并且输入不遵循索引顺序,甚至不更新值。

const TwoFactorPasswordInput: React.FC<TwoFactorInterface> = ({
  setTwoFactorStep
}) => {
  let elemRefs: any = useRef(
    Array.from({ length: 6 }, () => React.createRef())
  );

  const [totpCodeValues, setTotpCodeValues] = useState<string[]>([]);

  const [isDisabledButton, SetIsDisabledButton] = useState<boolean>(true);

  useEffect(() => {
    const autoTab = (e: any) => {
      const BACKSPACE_KEY = 8;
      const DELETE_KEY = 46;
      let tabindex = e.target.getAttribute('data-index');
      let elem: any = null;
      tabindex = Number(tabindex);

      if (e.keyCode === BACKSPACE_KEY) {
        elem = tabindex > 0 && elemRefs.current[tabindex - 1];
      } else if (e.keyCode !== DELETE_KEY) {
        elem =
          tabindex < elemRefs.current.length - 1 &&
          elemRefs.current[tabindex + 1];
      }
      if (elem) {
        elem.current.focus();
      }
    };

    document.addEventListener('keyup', autoTab);
    return () => {
      document.removeEventListener('keyup', autoTab);
    };
  }, []);

  const getValuesFromRefs = () => {
    elemRefs.current.map((element: any) => {
      console.log('=====INVOKED======');
      setTotpCodeValues(oldArray => [...oldArray, element.current.value]);
    });
  };

  const InputChar = (props: any) => {
    return (
      <Input
        data-testid="form-email"
        type="text"
        data-index={props.index}
        maxLength={1}
        autoComplete="off"
        name="email"
        value={totpCodeValues[props.index]}
        onChange={e => {
          let newCharValue = e.target.value;
          console.log('INDEX INSIDE ONCHANGE', props.index);
        }}
        onBlur={() => {
          if (props.index === 5) {
            getValuesFromRefs();
            SetIsDisabledButton(false);
          }
        }}
        required
        ref={props.reference}
      />
    );
  };

  const blocks = Array.from({ length: 6 }, (element, index) => (
    <InputChar key={index} index={index} reference={elemRefs.current[index]} />
  ));

  console.log('totp values', totpCodeValues);

  return (
    <Wrapper>
      {T.translate('twoFactorAuthentication.informCode')}
      <Form onSubmit={() => {}} data-testid="login-form">
        {blocks}
        <StyledButton data-testid="form-button" disabled={isDisabledButton}>
          {T.translate('twoFactorAuthentication.authenticate')}
        </StyledButton>
        <NeutralButton onClick={() => setTwoFactorStep(3)}>
          {' '}
          {T.translate('twoFactorAuthentication.return')}
        </NeutralButton>
      </Form>
    </Wrapper>
  );
};

现在,我正在尝试使用getValuesFromRefs通过引用获取所有值,但仍然没有成功,因为我需要更新SetIsDisabledButton,如果不更新,则输入就像魅力,但我需要更新并保留这些值。

1 个答案:

答案 0 :(得分:0)

我解决了从头开始构建此组件的问题,这是一个更简单的解决方案,如果有人使用refs在持久值中挣扎,我想在功能中共享该解决方案!

export const TwoFactorPasswordInput: React.FC<Props> = ({
  setTwoFactorStep,
  isAuthenticating,
  authenticationError,
  setAuthenticationStart,
  setAuthenticationSuccess,
  setAuthenticationFailure
}) => {
  let elemRefs: any = useRef(
    Array.from({ length: 6 }, () => React.createRef())
  );

  const [totpValues, setTotpValues] = useState<string[]>([]);

  const [isDisabledButton, setIsDisabledButton] = useState<boolean>(true);

  const setTotpValueAtIndex = (index: number) => (
    e: React.ChangeEvent<HTMLInputElement>
  ) => {
    const newValues = [...totpValues];
    newValues[index] = e.target.value;
    setTotpValues(newValues);
  };

  const totpValueString = totpValues.join('');

  useEffect(() => {
    const autoTab = (e: any) => {
      const BACKSPACE_KEY = 8;
      const DELETE_KEY = 46;
      let tabindex = e.target.getAttribute('data-index');
      let elem: any = null;
      tabindex = Number(tabindex);

      if (e.keyCode === BACKSPACE_KEY) {
        elem = tabindex > 0 && elemRefs.current[tabindex - 1];
      } else if (e.keyCode !== DELETE_KEY) {
        elem =
          tabindex < elemRefs.current.length - 1 &&
          elemRefs.current[tabindex + 1];
      }
      if (elem) {
        elem.current.focus();
      }
    };

    document.addEventListener('keyup', autoTab);
    return () => {
      document.removeEventListener('keyup', autoTab);
    };
  }, []);


  return (
    <Wrapper>
      {T.translate('twoFactorAuthentication.informCode')}
      <Form onSubmit={submitForm} data-testid="login-form">
        {[1, 2, 3, 4, 5, 6].map((_, index) => (
          <SingleDigitTotpInput
            key={index}
            data-index={index}
            onChange={setTotpValueAtIndex(index)}
            reference={elemRefs.current[index]}
          />
        ))}
        {!!authenticationError && (
          <Box>{T.translate('twoFactorAuthentication.error')}</Box>
        )}
        <StyledButton data-testid="form-button" disabled={isDisabledButton}>
          {T.translate('twoFactorAuthentication.authenticate')}
        </StyledButton>
        <NeutralButton
          onClick={() => {
            setAuthenticationFailure('');
            setTwoFactorStep(3);
          }}
        >
          {' '}
          {T.translate('twoFactorAuthentication.return')}
        </NeutralButton>
      </Form>
    </Wrapper>
  );
};