我正在尝试使用React JS开发一个相当简单的电子邮件模板创建者。我正在使用" react-sortable-hoc"库作为处理页面上元素排序的手段。
目标是允许用户创建"行",重新排列行,并在每行内,有多个"列"可以包含图像,文本框等组件......
但我仍然遇到与Sortable库相同的问题。表单字段不能维持自己的状态"被上下拖动时React JS中的组件状态似乎在可拖动组件中丢失。我在使用JQuery UI的Sortable时遇到过类似的问题,但它需要一个同样荒谬的解决方案。是否常常发现表单字段很难重新排列?
作为"概念证明",我使用一个复杂的JSON对象,它将所有信息存储在Letter.js组件中,并将其作为Props传递下来,然后传递给每个组件。但正如你所知,这变得很麻烦。
以下是我的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;
以下是Row的一个例子:
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效果。但是,我无法看到将一切都作为道具传递下来的方法。