Мне нужно получить данные выделения текста из TextField Material-ui. Данные выделения текста должны включать начало выделения, конец выделения и выделенный текст.

Мне удалось добраться до следующего с помощью createRefs

import React, { createRef } from "react";
import { TextField, Button, Box, Grid } from "@material-ui/core";

export default class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      selStart: undefined,
      selEnd: undefined,
      selValue: undefined
    };
    this.onSaveSelectionClicked = this.onSaveSelectionClicked.bind(this);
    this.textFieldRef = createRef();
  }

  onSaveSelectionClicked(ev) {
    // Somehow obtain selection start, end, and value
    // then update state
    const { selectionStart, selectionEnd, value } = this.textFieldRef.current;
    this.setState({
      selStart: selectionStart,
      selEnd: selectionEnd,
      selValue: value.substring(selectionStart, selectionEnd)
    });
  }

  render() {
    return (
      <Box p="2em">
        <Grid container spacing={2}>
          <Grid item xs={12}>
            <TextField
              name="essay1"
              label="Essay 1"
              multiline
              rows={3}
              fullWidth
              inputRef={this.textFieldRef}
            />
          </Grid>
          <Grid item xs={12}>
            <TextField
              name="essay2"
              label="Essay 2"
              multiline
              rows={3}
              fullWidth
              inputRef={this.textFieldRef}
            />
          </Grid>
          <Grid item xs={12}>
            <Button variant="contained" onClick={this.onSaveSelectionClicked}>
              Save selection
            </Button>
          </Grid>
          <Grid item xs={12}>
            <pre>{JSON.stringify(this.state)}</pre>
          </Grid>
        </Grid>
      </Box>
    );
  }
}

Что дает мне следующее

enter image description here

Проблема в том, что это не работает с несколькими TextField. Свойство current ссылок всегда будет ссылаться на текстовое поле «Эссе 2».

Есть ли лучший способ подойти к этой проблеме?

Вот ссылка на codesandbox для приведенного выше кода.

2
bluearth 11 Апр 2020 в 15:33

2 ответа

Лучший ответ

Совсем не нужны ссылки, таким образом, выбор меняется динамически.
Измените конструктор следующим образом:

constructor(props) {
    super(props);
    this.state = {
      selStart: undefined,
      selEnd: undefined,
      selValue: undefined
    };
    this.onSelectionChange = this.onSelectionChange.bind(this);
  }

Добавить обработчик:

 onSelectionChange(e) {
    const { selectionStart, selectionEnd, value } = e.target;

    this.setState({
      selStart: selectionStart,
      selEnd: selectionEnd,
      selValue: value.substring(selectionStart, selectionEnd)
    });
  }

А затем в ваших текстовых полях Просто установите событие onMouseUp (или onBlur, если вы хотите обновить на lostFocus):

onMouseUp={this.onSelectionChange}

При этом ваш исходный код будет выглядеть следующим образом:

export default class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      selStart: undefined,
      selEnd: undefined,
      selValue: undefined
    };
    this.onSelectionChange = this.onSelectionChange.bind(this);
  }

  onSelectionChange(e) {
    const { selectionStart, selectionEnd, value } = e.target;

    this.setState({
      selStart: selectionStart,
      selEnd: selectionEnd,
      selValue: value.substring(selectionStart, selectionEnd)
    });
  }

  render() {
    return (
      <Box p="2em">
        <Grid container spacing={2}>
          <Grid item xs={12}>
            <TextField
              name="essay1"
              label="Essay 1"
              multiline
              rows={3}
              fullWidth
              onMouseUp={this.onSelectionChange}
            />
          </Grid>
          <Grid item xs={12}>
            <TextField
              name="essay2"
              label="Essay 2"
              multiline
              rows={3}
              fullWidth
              onMouseUp={this.onSelectionChange}
            />
          </Grid>
        </Grid>
      </Box>
    );
  }
}
3
amin mohammadi 11 Апр 2020 в 14:18

Вам нужно создать ссылку для каждого dom-узла, к которому вы хотите получить доступ.

export default class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      selStart: undefined,
      selEnd: undefined,
      selValue: undefined
    };
    this.onSaveSelectionClicked = this.onSaveSelectionClicked.bind(this);
    // Don't forget to attach refs to dom nodes accordingly!
    this.firstTextFieldRef = createRef();
    this.secondTextFieldRef = createRef();
  }
  // Rest of your component...
}

Затем вы можете прослушать событие фокуса каждой текстовой области, чтобы узнать, какой из них был выбран в последний раз (и, следовательно, содержит выделенный текст), или вы можете полагаться на тот факт, что вы не можете выделить текст в двух текстовых областях одновременно (что является не совсем верно).

Вариант фокуса чище ИМО:

// Using function composition to avoid logic duplication
handleTextFieldFocus = ref => () => {
  this.setState({ lastSelectedField: ref })
}

// Attach to your first text area
handleFirstFieldFocus = handleTextFieldFocus(this.firstTextFieldRef)

// Attach to your second text area
handleSecondFieldFocus = handleTextFieldFocus(this.secondTextFieldRef)

onSaveSelectionClicked() {
  // We are getting the values from the last focused input
  const {
    selectionStart,
    selectionEnd,
    value
  } = this.state.lastSelectedField.current;

  this.setState({
    selStart: selectionStart,
    selEnd: selectionEnd,
    selValue: value.substring(selectionStart, selectionEnd)
  });
}

Второй вариант означает проверку значений каждой ссылки перед установкой состояния:

onSaveSelectionClicked() {
  let {
    selectionStart,
    selectionEnd,
    value
  } = this.firstTextFieldRef.current;

  // If there is no selected text in the first text field
  // we will assume that there must be on the second text area
  if (!firstSelectionStart && !firstSelectionEnd) {
    selectionStart = this.secondTextFieldRef.current.selectionStart
    selectionEnd = this.secondTextFieldRef.current.selectionEnd
    value = this.secondTextFieldRef.current.value
  }

  this.setState({
    selStart: selectionStart,
    selEnd: selectionEnd,
    selValue: value.substring(selectionStart, selectionEnd)
  });
}
1
Giorgio Zanni 11 Апр 2020 в 13:40