Łączenie zdarzeń - react, javascript, onMouseDown & onMouseEnter

0

Cześć,

Mam taki problem: potrzebuję "powiązać" ze sobą zdarzenia. Tj. Mamy np. 16 divów ułożonych w szachownicę, chciałbym wywołać jakieś zdarzenie na wybranych divach w następujący sposób:

  1. Najeżdżamy na dowolnego diva myszką
  2. Robimy mouse down, event się uruchamia
  3. Najeżdżamy myszką na kolejne wybrane divy i na każdym z nich po mouse enter event się uruchamia. UWAGA: event mouse enter uruchamia się tylko, gdy jest wciśnięty przycisk myszki
  4. Puszczamy przycisk myszki - event nie może się już wykonać, dopóki ponownie nie zrobimy mouse down, nie wciśniemy przycisku.

Podejrzewam, że brakuje mi odpowiedniego słownictwa, bo niestety nie udało mi się znaleźć w googlach, jak nazywa się tego typu łączenie zdarzeń. Podpowie ktoś? Czy to w ogóle jest wykonalne poprzez same event listenery? Tak na logikę to chyba musi być wykonalne właśnie przez event listenery :D.

2

Oczywiście masz na myśli: mouseover.
Użyj zmiennej globalnej przełączanej przez mousedown mouseup. I wtedy zależnie od jej stanu różnie reaguj na mouseover.

0

Dlaczego mouse over, a nie enter? Jest to aż tak istotne w tym wypadku? Różnica dotyczy tylko dzieci, a tu ich nie ma, więc bez różnicy, tak :)?

I dzięki, rano powalczę z ze zmienną globalną :)

2

ustawiasz flagę po prostu (jakąś zmienną) w czasie mouseDown np. isDown = true, potem sprawdzasz mouseMove czy flaga jest ustawiona, a w mouseUp wyłączasz flagę.

2

@Freja Draco, @LukeJL - sam chciałem też napisać coś ze zmienną pomocniczą/flagą, która będzie ustawiana w chwili kliknięcia, a następnie, przy wchodzeniu kursorem na dane elementy, nastąpi sprawdzenie stanu tej flagi. Ale dałem sobie spokój, bo uznałem, że to prowizorka, zaraz zostanę zjechany, że tak się nie robi, że powinno się jakieś hooki ustawiać itp :D

Widać czasami warto zaufać swojej intuicji/pierwszej myśli ;)

1
Freja Draco napisał(a):

Oczywiście masz na myśli: mouseover.
Użyj zmiennej globalnej przełączanej przez mousedown mouseup. I wtedy zależnie od jej stanu różnie reaguj na mouseover.

Gdy ostatnio coś takiego robiłem, to zrobiłem to w podobny sposób. Niestety nie znalazłem jeszcze biblioteki JS, która pozwalałby takie rzeczy szybko i łatwo ogarnąć. Kod trzeba pisać samemu i niestety liczyć się z różnicami pomiędzy przeglądarkami np. w niektórych przeglądarkach kolejność wywoływania zdarzeń myszy jest inna i trzeba obsłużyć takie przypadki jeżeli kod ma działać na różnych przeglądarkach.

0

Mogę jeszcze prosić o przykład, jak się deklaruje taką zmienną globalną z eventem zależnym od mouseDown?

Domyślam się, że należy to zrobić w komponencie funkcyjnym, ale coś nie wychodzi powiązanie tego.

0

zmienna_globalna = 123;

Bez żadnych var / let przed.

0

Niee, wtedy woła, że jest niezdefiniowana zmienna.

Bardziej chodzi mi o przykład, jak poza JSX stworzyć funkcję, listenera, który będzie w tej zmiennej globalnej zapisywal stan, czy przycisk jest kliknięty, czy nie.

0

Nie znam Reacta i nie kumam problemu, ale w JS wygląda to tak:
https://www.w3schools.com/jsref/event_onmousedown.asp

0

W zwykłym JS to nie miałbym problemu :) tutaj potrzebuję to zrobić w Reacie. I wiązanie funkcji jest inne po prostu. Albo jestem jeszcze trochę głupi :)

0

W JSX funkcje wołasz tak

<g onMouseDown={this.onDown}>

W komponencie funkcyjnym ofc this jest zbędne. W funkcji onDown ustaw sobie zmienną.

0

Przepraszam, jeszcze za małą mam wiedzę w tych tematach :).

Z tym g przed onMouseDown chodzi Ci o to, żeby wrzucić to w jakiegoś taga, który mi pasuje? Bo taga g, to ja nie znam :D Gadanie na sucho chyba nie ma sensu, poniżej link do repo. W komponencie bazowym Square wrzuciłem to jak to widzę na ten moment. Oczywiście niestety nie działa dobrze :D

Jakby coś było niejasne, albo bez sensu, to proszę o feedback :) Tutaj to widzę tak, że mamy w stanie właściwość isMouseDown: false. Zdarzenie onMouseDown przełącza go na true, po czym zdarzenie onMouseEnter odpala handleMove, które sprawdza isMouseDown, jak jest true, to odpala this.props.onClick(), które przekazuje wyżej w hierarchii, że się wydarzyło i ma robić co należy.

Widzisz gdzie tutaj to źle zrobiłem? Mogę prosić o radę :)?

Repo

3

Ustaw flagę jako state nadrzędnego kontenera i przekazuj ją (jak i funkcję ją modyfikującą) przez propsy. Nie trzeba żadnych kombinacji - wystarczy podstawowa funkcjonalność Reacta.

Przykład:

// Container.js

import React from 'react';
import Item from './Item';

class Container extends React.Component {
  constructor () {
    this.state = {
      isMouseDown: false,
    }

    this.toggleMouseDown = this.toggleMouseDown.bind(this)
  }

  toggleMouseDown() {
    this.setState({isMouseDown: !this.state.isMouseDown})
  }

  render() {
    return <div className="container">
      {[1, 2, 3, 4, 5, 6, 7, 8, 9].map((key) => (
        <Item
          key={key}
          toggleMouseDown={this.toggleMouseDown}
          isMouseDown={this.state.isMouseDown}
        />
      ))}
    </div>
  }
}

export default Container
// Item.js

import React from 'react'

class Item extends React.Component {
  constructor() {
    this.state = {
      selected: false,
    }

    this.select = this.select.bind(this)
    this.selectIfMouseDown = this.selectIfMouseDown.bind(this)
  }

  select() {
    this.setState({ selected: true })
  }

  selectIfMouseDown() {
    if (this.props.isMouseDown) {
      this.select()
    }
  }

  render() {
    return (
      <div
        className={`item ${this.state.selected ? 'selected' : ''}`}
        onMouseDown={() => {
          this.props.toggleMouseDown()
          this.select()
        }}
        onMouseUp={this.props.toggleMouseDown}
        onMouseEnter={this.selectIfMouseDown}
      >
          {this.props.isMouseDown ? 'down': 'up'}
      </div>
    )
  }
}

export default Item

Live demo: https://stackblitz.com/edit/react-mousedown-1


Dla tych co proponują użycie globalnej flagi, jest to złe bo:

  1. Zaśmieca globalny namespace
  2. Jest ciężkie w debugowaniu
  3. Nie jest potrzebne
  4. Nie działa (a przynajmniej nie tak jak by się wydawało)

Ostatni punkt wymaga rozwinięcia:

React by przerenderować komponent musi wiedzieć, że zmiana zaszła. React nie nasłuchuje wszelkich zmian w kontekście JS, funkcję render() komponentu trigeruje tylko zmiana stanu przez setState i zmiana propsów.
Od biedy da się zrobić takim hackiem proste rzeczy, ale nie da się synchronizować stanu pomiędzy komponentami. Poniższy przykład "działa", ale tylko dlatego, że oprócz zmiany zmiennej globalnej jest także zmieniany stan komponentu this.setState({ selected: true }) - zauważ, że pozostałe komponenty "nie widzą" stanu myszy, dopóki na nie nie najedzie (w ogóle cyrk się robi jak dłużej poklikasz - mysz Schrodingera, nie wiesz czy wciśnięta czy nie póki nie spojrzysz na nią):

https://stackblitz.com/edit/react-mousedown-2


PS
@cerrato Miałeś dobre przeczucie, po prostu musiałeś troszkę poczekać aż ktoś to zjedzie :P

0

@Maciej Cąderek: Bardzo Ci dziękuję :). W repo które udostępniłem tak właśnie zacząłem działać z tym stanem, przeoczyłem jedynie to, że komponenty inne faktycznie nie wiedzą o tym co się dzieje w konkretnym wybranym wcześniej (klikniętym) komponencie. Tu leżał mój główny problem. Dzięki jeszcze raz :)

1 użytkowników online, w tym zalogowanych: 0, gości: 1