React Hooks onChange не принимает ввод - PullRequest
0 голосов
/ 13 марта 2020

У меня странная ошибка, которая возникает только иногда - onChange срабатывает, но не меняет значение. Затем, если я щелкну за пределами ввода с помощью функции onChange, а затем снова нажму внутри поля ввода, функция onChange начнет работать.

Функция onChange выглядит следующим образом:

const handleBarAmountChange = (event) => {
    let newWidthAmount = event.target.value / 10;
    setNewWidth(newWidthAmount);
    setNewBarAmount(event.target.value);
};

A parent div использует ссылку с useRef, которая передается этой функции:

import { useEffect, useState } from 'react';
const useMousePosition = (barRef, barInputRef, barContainerRef) => {
    const [ mouseIsDown, setMouseIsDown ] = useState(null);

    useEffect(() => {
        const setMouseDownEvent = (e) => {
            if (e.which == 1) {
                if (barContainerRef.current.contains(e.target) && !barInputRef.current.contains(e.target)) {
                    setMouseIsDown(e.clientX);
                } else if (!barInputRef.current.contains(e.target)) {
                    setMouseIsDown(null);
                }
            }
        };

        window.addEventListener('mousemove', setMouseDownEvent);
        return () => {
            window.removeEventListener('mousemove', setMouseDownEvent);
        };
    }, []);
    return { mouseIsDown };
};

Конфликт onChange каким-либо образом конфликтует с eventListener?

Как мне обойти это?

Ответы [ 2 ]

1 голос
/ 14 марта 2020

Было несколько синтаксических ошибок и отсутствующих зависимостей хуков, которые стали причиной ваших ошибок. Однако вы можете немного упростить свой код с помощью нескольких настроек.

При использовании состояния, которое зависит от другого состояния, я рекомендую объединить его в объект и использовать функцию обратного вызова для его синхронного обновления: setState(prevState => ({ ...prevState, example: "newValue" }) , Это похоже на то, как this.setState(); работает в компоненте на основе классов. Используя один объект и распространяя его свойства ({ ...prevState }), мы можем перезаписать одно из его свойств, переопределив одно из них ({ ...prevState, newWidth: 0 }). Этот способ гарантирует, что значения синхронизируются c друг с другом.

Пример ниже следует единому шаблону объекта, упомянутому выше, где newWidth, newBarAmount и isDragging являются свойствами один объект (state). Затем в примере используется setState для синхронного обновления / переопределения значений. Кроме того, ссылки были удалены и позволяют перетаскивать панель за окно (если вы этого не хотите, вам нужно ограничить ее в пределах barContainerRef, как вы делали ранее). В примере также проверяется логическое значение state.isDragging, когда пользователь щелкает левой кнопкой мыши и удерживает ее на панели. Как только левый щелчок отпущен, перетаскивание отключено.

Вот рабочий пример:

Edit Bar & Input Updates


компоненты / бар / индекс. js

import React, { useEffect, useState, useCallback } from "react";
import PropTypes from "prop-types";
import "./Bar.css";

function Bar({ barName, barAmount, colour, maxWidth }) {
  const [state, setState] = useState({
    newWidth: barAmount / 2,
    newBarAmount: barAmount,
    isDragging: false
  });

  // manual input changes
  const handleBarAmountChange = useCallback(
    ({ target: { value } }) => {
      setState(prevState => ({
        ...prevState,
        newWidth: value / 2,
        newBarAmount: value
      }));
    },
    []
  );

  // mouse move
  const handleMouseMove = useCallback(
    ({ clientX }) => {
      if (state.isDragging) {
        setState(prevState => ({
          ...prevState,
          newWidth: clientX > 0 ? clientX / 2 : 0,
          newBarAmount: clientX > 0 ? clientX : 0
        }));
      }
    },
    [state.isDragging]
  );

  // mouse left click hold
  const handleMouseDown = useCallback(
    () => setState(prevState => ({ ...prevState, isDragging: true })),
    []
  );

  // mouse left click release
  const handleMouseUp = useCallback(() => {
    if (state.isDragging) {
      setState(prevState => ({
        ...prevState,
        isDragging: false
      }));
    }
  }, [state.isDragging]);

  useEffect(() => {
    window.addEventListener("mousemove", handleMouseMove);
    window.addEventListener("mouseup", handleMouseUp);

    return () => {
      window.removeEventListener("mousemove", handleMouseMove);
      window.removeEventListener("mouseup", handleMouseUp);
    };
  }, [handleMouseMove, handleMouseUp]);

  return (
    <div className="barContainer">
      <div className="barName">{barName}</div>
      <div
        style={{ cursor: state.isDragging ? "grabbing" : "pointer" }}
        onMouseDown={handleMouseDown}
        className="bar"
      >
        <svg
          width={state.newWidth > maxWidth ? maxWidth : state.newWidth}
          height="40"
          fill="none"
          xmlns="http://www.w3.org/2000/svg"
          colour={colour}
        >
          <rect width={state.newWidth} height="40" fill={colour} />
        </svg>
      </div>
      <div className="barAmountUnit">£</div>
      <input
        className="barAmount"
        type="number"
        value={state.newBarAmount}
        onChange={handleBarAmountChange}
      />
    </div>
  );
}

// default props (will be overridden if defined)
Bar.defaultProps = {
  barAmount: 300,
  maxWidth: 600
};

// check that passed in props match patterns below
Bar.propTypes = {
  barName: PropTypes.string,
  barAmount: PropTypes.number,
  colour: PropTypes.string,
  maxWidth: PropTypes.number
};

export default Bar;
1 голос
/ 13 марта 2020

React использует SyntheticEvent и Event Pooling, от до c:

Объединение событий

SyntheticEvent объединяется. Это означает, что объект SyntheticEvent будет использоваться повторно, и все свойства будут аннулированы после вызова обратного вызова события. Это из соображений производительности. Таким образом, вы не можете получить доступ к событию асинхронным способом.

Вы можете вызвать event.persist() для события или сохранить значение в новой переменной и использовать его следующим образом:

const handleBarAmountChange = (event) => {
  // event.persist(); 
  // Or
  const { value } = event.target;

  let newWidthAmount = value / 10;
  setNewWidth(newWidthAmount);
  setNewBarAmount(value);
};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...