Реагировать поля сортируемой формы JS как компоненты

Я пытаюсь разработать довольно упрощенный шаблон E-Mail для React JS. Я использую библиотеку "act-sortable-hoc"в качестве средства для обработки порядка элементов на странице.

Цель состоит в том, чтобы позволить пользователям создавать "Строки", переупорядочивать строки, и в каждой строке иметь несколько "Столбцов", которые могут содержать такие компоненты, как изображения, текстовые поля и т. Д.

Но я продолжаю сталкиваться с той же проблемой с библиотеками Sortable. Поля формы не могут поддерживать свое собственное "состояние" при перетаскивании вверх или вниз. Кажется, что состояние компонента в React JS теряется, когда он находится в перетаскиваемом компоненте. Я сталкивался с подобными проблемами с Sortable JQuery UI, но это требовало столь же нелепого решения. Часто ли обнаруживают, что поля формы просто очень сложно переставить?

В качестве "доказательства концепции" я использую сложный объект JSON, который хранит всю информацию в компоненте Letter.js и передает ее в виде реквизитов, которые затем передаются каждому компоненту. Но, как вы можете сказать, это становится громоздким.

Вот пример моего компонента Letter, который обрабатывает объект JSON и сортировку строк:

import React, {Component} from 'react';
import {render} from 'react-dom';
import {
  SortableContainer, 
  SortableElement, 
  arrayMove
} from 'react-sortable-hoc';
import Row from './Row';

const SortableItem = SortableElement(({row, rowIndex, onChange, addPart}) => {
  return (
    <li>
      <Row 
        row={row} 
        rowIndex={rowIndex} 
        onChange={onChange}
        addPart={addPart} />
    </li>
  )
});

const SortableList = SortableContainer(({rows, onChange, addPart}) => {
  return (
    <ul id="sortableList">
      {rows.map((row, index) => {
        return (
          <SortableItem
            key={`row-${index}`} 
            index={index} 
            row={row}
            rowIndex={index}
            onChange={onChange}
            addPart={addPart}
           /> )
      })}
    </ul>
  );
});

class Letter extends Component {
  constructor(props) {
    super(props);
    this.state = {
      rows: [],
    }

    this.onSortEnd    = this.onSortEnd.bind(this);
    this.onChange     = this.onChange.bind(this);
    this.addRow       = this.addRow.bind(this);
    this.addPart      = this.addPart.bind(this);
  }

  addPart(event, index, value, rowIndex, columnIndex) {
    console.log(value);
    var part = {};
    if(value === 'Text') {
      part = {
        type: 'Text',
        value: ''
      }
    } else if(value === 'Image') {
      part = {
        type: 'Image',
        value: ''
      }
    } else {
      part = {
        type: 'Empty',
      }
    }
    const { rows  } = this.state;
    rows[rowIndex][columnIndex] = part;
    this.setState({ rows: rows })
  }

  onChange(text, rowIndex, columnIndex) {
    const { rows } = this.state;
    const newRows = [...rows];
    newRows[rowIndex][columnIndex].value = text;
    this.setState({ rows: newRows });
  }

  addRow(columnCount) {
    var rows = this.state.rows.slice();
    var row = [];
    for(var i = 0; i < columnCount; i++) {
      var part = {
        type: 'Empty',
      }
      row.push(part);
    }
    rows.push(row);
    this.setState({ rows: rows })
  }

  onSortEnd = ({oldIndex, newIndex}) => {
    this.setState({
      rows: arrayMove(this.state.rows, oldIndex, newIndex),
    });
  };

  render() {
    console.log(JSON.stringify(this.state.rows));
    const SideBar = (
      <div className="sideBar">
        <h3>Add a Row</h3>
        <button className="uw-button" onClick={() => this.addRow(1)}>1 - Column</button><br/><br/>
        <button className="uw-button" onClick={() => this.addRow(2)}>2 - Column</button><br/><br/>
        <button className="uw-button" onClick={() => this.addRow(3)}>3 - Column</button>
        <hr />
      </div>
    );

    if(this.state.rows.length <= 0) {
      return (
        <div className="grid">
          <p>This E-Mail is currently empty! Add some components to make a template.</p>
          {SideBar}
        </div>
      )
    }
    return (
      <div className="grid">
        <SortableList 
          rows={this.state.rows} 
          onChange={this.onChange}
          addPart={this.addPart}
          lockAxis="y"
          useDragHandle={true}
          onSortStart={this.onSortStart} 
          onSortMove={this.onSortMove}
          onSortEnd={this.onSortEnd} 
          shouldCancelStart={this.shouldCancelStart} />
        {SideBar}
      </div>
    );
  }
}

export default Letter;

А вот пример строки:

import React, { Component } from 'react';
import { Text, Image } from './components/';

import { SortableHandle } from 'react-sortable-hoc';

import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import { Card, CardActions, CardHeader, CardMedia, CardTitle, CardText } from 'material-ui/Card';
import DropDownMenu from 'material-ui/DropDownMenu';
import MenuItem from 'material-ui/MenuItem';


const DragHandle = SortableHandle(() => <span className="dragHandle"></span>);


class Row extends Component {
  constructor(props) {
    super(props);
  }

  render() {
    if(this.props.row !== undefined && this.props.row.length > 0) {
      const row = this.props.row.map((column, columnIndex) => {
        if(column.type === 'Empty') {
          return (
            <MuiThemeProvider key={columnIndex}>
              <div className="emptyColumn">
                <Card>
                  <DragHandle />
                  <CardTitle title="Empty Component"/>
                  <DropDownMenu value={"Empty"} onChange={(event, index, value) => this.props.addPart(event, index, value, this.props.rowIndex, columnIndex)}>
                    <MenuItem value={"Empty"} primaryText="Empty" />
                    <MenuItem value={"Text"} primaryText="Textbox" />
                    <MenuItem value={"Image"} primaryText="Image" />
                  </DropDownMenu>
                </Card>
              </div>
            </MuiThemeProvider>
          )
        } else if(column.type === 'Text') {
           return (
            <MuiThemeProvider key={columnIndex}>
              <div className="textColumn">
                <Card>
                  <DragHandle />
                  <CardTitle title="Textbox"/>
                  <DropDownMenu value={"Text"} onChange={(event, index, value) => this.props.addPart(event, index, value, this.props.rowIndex, columnIndex)}>
                    <MenuItem value={"Empty"} primaryText="Empty" />
                    <MenuItem value={"Text"} primaryText="Textbox" />
                    <MenuItem value={"Image"} primaryText="Image" />
                  </DropDownMenu>
                  <Text 
                    value={this.props.row[columnIndex].value} 
                    onChange={this.props.onChange} 
                    columnIndex={columnIndex}
                    rowIndex={this.props.rowIndex} />
                </Card>
              </div>
            </MuiThemeProvider>
          )         
        } else if(column.type === 'Image') {
           return (
            <MuiThemeProvider key={columnIndex}>
              <div className="textColumn">
                <Card>
                  <DragHandle />
                  <CardTitle title="Image"/>
                  <DropDownMenu value={"Image"} onChange={(event, index, value) => this.props.addPart(event, index, value, this.props.rowIndex, columnIndex)}>
                    <MenuItem value={"Empty"} primaryText="Empty" />
                    <MenuItem value={"Text"} primaryText="Textbox" />
                    <MenuItem value={"Image"} primaryText="Image" />
                  </DropDownMenu>
                  <Image 
                    columnIndex={columnIndex}
                    rowIndex={this.props.rowIndex} />
                </Card>
              </div>
            </MuiThemeProvider>
          )         
        }
      })


      return (
        <div className="row">
          {row}
        </div>
      )
    }

    return <p>No components</p>;

  }
}

export default Row;

Наконец, так выглядит Text.js

import React, { Component } from 'react';
import ReactQuill from 'react-quill'; 

class Text extends Component {
  constructor(props) {
    super(props);
  }

  render() {
    return (
      <ReactQuill value={this.props.value}
                  onChange={(text) => this.props.onChange(text, this.props.rowIndex, this.props.columnIndex)} />
    )
  }
}

export default Text;

Поэтому мне постоянно приходится передавать нелепые параметры в функции onChange и другие функции, чтобы обеспечить сохранение состояния при сортировке и редактировании. Итак, как я должен справиться с этим? Я не хочу, чтобы Letter.js (в основном это App.js) обрабатывал все мои данные. Я хочу, чтобы каждый компонент обрабатывал свои собственные. Я хочу, чтобы Text.js обрабатывал эффекты onChange своего текста. Но я просто не вижу способа передать все как реквизит.

0 ответов

Другие вопросы по тегам