jQuery UI 可排序 React.js 越野车
jQuery UI Sortable with React.js buggy
我在 React 中有一个可排序列表,由 jQuery UI 提供支持。当我在列表中拖放一个项目时,我想更新数组以便将列表的新顺序存储在那里。然后使用更新后的数组重新呈现页面。即 this.setState({data: _todoList});
目前,当您拖放一个项目时,jQuery UI DnD 工作,但项目在 UI 中的位置不会改变,即使页面重新- 使用更新后的数组呈现。即在 UI 中,即使定义其位置的数组已成功更新,该项目仍会恢复到其在列表中的位置。
如果您拖放项目两次,它就会移动到正确的位置。
// Enable jQuery UI Sortable functionality
$(function() {
$('.bank-entries').sortable({
axis: "y",
containment: "parent",
tolerance: "pointer",
revert: 150,
start: function (event, ui) {
ui.item.indexAtStart = ui.item.index();
},
stop: function (event, ui) {
var data = {
indexStart: ui.item.indexAtStart,
indexStop: ui.item.index(),
accountType: "bank"
};
AppActions.sortIndexes(data);
},
});
});
// This is the array that holds the positions of the list items
var _todoItems = {bank: []};
var AppStore = assign({}, EventEmitter.prototype, {
getTodoItems: function() {
return _todoItems;
},
emitChange: function(change) {
this.emit(change);
},
addChangeListener: function(callback) {
this.on(AppConstants.CHANGE_EVENT, callback);
},
sortTodo: function(todo) {
// Dynamically choose which Account to target
targetClass = '.' + todo.accountType + '-entries';
// Define the account type
var accountType = todo.accountType;
// Loop through the list in the UI and update the arrayIndexes
// of items that have been dragged and dropped to a new location
// newIndex is 0-based, but arrayIndex isn't, hence the crazy math
$(targetClass).children('form').each(function(newIndex) {
var arrayIndex = Number($(this).attr('data-array-index'));
if (newIndex + 1 !== arrayIndex) {
// Update the arrayIndex of the element
_todoItems[accountType][arrayIndex-1].accountData.arrayIndex = newIndex + 1;
}
});
// Sort the array so that updated array items move to their correct positions
_todoItems[accountType].sort(function(a, b){
if (a.accountData.arrayIndex > b.accountData.arrayIndex) {
return 1;
}
if (a.accountData.arrayIndex < b.accountData.arrayIndex) {
return -1;
}
// a must be equal to b
return 0;
});
// Fire an event that re-renders the UI with the new array
AppStore.emitChange(AppConstants.CHANGE_EVENT);
},
}
function getAccounts() {
return { data: AppStore.getTodoItems() }
}
var Account = React.createClass({
getInitialState: function(){
return getAccounts();
},
componentWillMount: function(){
AppStore.addChangeListener(this._onChange);
// Fires action that triggers the initial load
AppActions.loadComponentData();
},
_onChange: function() {
console.log('change event fired');
this.setState(getAccounts());
},
render: function(){
return (
<div className="component-wrapper">
<Bank data={this.state.data} />
</div>
)
}
});
由于 React 使用虚拟 DOM,您必须使用函数 React.findDOMNode() 来访问实际的 DOM 元素。
我会在组件的 componentDidMount 方法中调用 jQuery UI 函数,因为您的元素必须已经呈现才能访问。
// You have to add a ref attribute to the element with the '.bank-entries' class
$( React.findDOMNode( this.refs.bank_entries_ref ) ).sortable( /.../ );
Documentation - Working with the browser(你想知道的都在这里)
希望这有意义并能解决您的问题
jQuery UI Sortable 不适用于 React 的原因是因为它直接改变了 DOM,这是一个很大的 no no 在反应中。
要使其正常工作,您必须修改 jQuery UI Sortable 以便保留 DnD 功能,但是当您删除元素时,它 不会修改DOM。相反,它可以触发一个事件,该事件触发具有元素新位置的 React 渲染。
诀窍是在 Sortable 的 stop
事件中调用 sortable('cancel')
,然后让 React 更新 DOM.
componentDidMount() {
this.domItems = jQuery(React.findDOMNode(this.refs["items"]))
this.domItems.sortable({
stop: (event, ui) => {
// get the array of new index (http://api.jqueryui.com/sortable/#method-toArray)
const reorderedIndexes = this.domItems.sortable('toArray', {attribute: 'data-sortable'})
// cancel the sort so the DOM is untouched
this.domItems.sortable('cancel')
// Update the store and let React update (here, using Flux)
Actions.updateItems(Immutable.List(reorderedIndexes.map( idx => this.state.items.get(Number(idx)))))
}
})
}
我在 React 中有一个可排序列表,由 jQuery UI 提供支持。当我在列表中拖放一个项目时,我想更新数组以便将列表的新顺序存储在那里。然后使用更新后的数组重新呈现页面。即 this.setState({data: _todoList});
目前,当您拖放一个项目时,jQuery UI DnD 工作,但项目在 UI 中的位置不会改变,即使页面重新- 使用更新后的数组呈现。即在 UI 中,即使定义其位置的数组已成功更新,该项目仍会恢复到其在列表中的位置。
如果您拖放项目两次,它就会移动到正确的位置。
// Enable jQuery UI Sortable functionality
$(function() {
$('.bank-entries').sortable({
axis: "y",
containment: "parent",
tolerance: "pointer",
revert: 150,
start: function (event, ui) {
ui.item.indexAtStart = ui.item.index();
},
stop: function (event, ui) {
var data = {
indexStart: ui.item.indexAtStart,
indexStop: ui.item.index(),
accountType: "bank"
};
AppActions.sortIndexes(data);
},
});
});
// This is the array that holds the positions of the list items
var _todoItems = {bank: []};
var AppStore = assign({}, EventEmitter.prototype, {
getTodoItems: function() {
return _todoItems;
},
emitChange: function(change) {
this.emit(change);
},
addChangeListener: function(callback) {
this.on(AppConstants.CHANGE_EVENT, callback);
},
sortTodo: function(todo) {
// Dynamically choose which Account to target
targetClass = '.' + todo.accountType + '-entries';
// Define the account type
var accountType = todo.accountType;
// Loop through the list in the UI and update the arrayIndexes
// of items that have been dragged and dropped to a new location
// newIndex is 0-based, but arrayIndex isn't, hence the crazy math
$(targetClass).children('form').each(function(newIndex) {
var arrayIndex = Number($(this).attr('data-array-index'));
if (newIndex + 1 !== arrayIndex) {
// Update the arrayIndex of the element
_todoItems[accountType][arrayIndex-1].accountData.arrayIndex = newIndex + 1;
}
});
// Sort the array so that updated array items move to their correct positions
_todoItems[accountType].sort(function(a, b){
if (a.accountData.arrayIndex > b.accountData.arrayIndex) {
return 1;
}
if (a.accountData.arrayIndex < b.accountData.arrayIndex) {
return -1;
}
// a must be equal to b
return 0;
});
// Fire an event that re-renders the UI with the new array
AppStore.emitChange(AppConstants.CHANGE_EVENT);
},
}
function getAccounts() {
return { data: AppStore.getTodoItems() }
}
var Account = React.createClass({
getInitialState: function(){
return getAccounts();
},
componentWillMount: function(){
AppStore.addChangeListener(this._onChange);
// Fires action that triggers the initial load
AppActions.loadComponentData();
},
_onChange: function() {
console.log('change event fired');
this.setState(getAccounts());
},
render: function(){
return (
<div className="component-wrapper">
<Bank data={this.state.data} />
</div>
)
}
});
由于 React 使用虚拟 DOM,您必须使用函数 React.findDOMNode() 来访问实际的 DOM 元素。
我会在组件的 componentDidMount 方法中调用 jQuery UI 函数,因为您的元素必须已经呈现才能访问。
// You have to add a ref attribute to the element with the '.bank-entries' class
$( React.findDOMNode( this.refs.bank_entries_ref ) ).sortable( /.../ );
Documentation - Working with the browser(你想知道的都在这里)
希望这有意义并能解决您的问题
jQuery UI Sortable 不适用于 React 的原因是因为它直接改变了 DOM,这是一个很大的 no no 在反应中。
要使其正常工作,您必须修改 jQuery UI Sortable 以便保留 DnD 功能,但是当您删除元素时,它 不会修改DOM。相反,它可以触发一个事件,该事件触发具有元素新位置的 React 渲染。
诀窍是在 Sortable 的 stop
事件中调用 sortable('cancel')
,然后让 React 更新 DOM.
componentDidMount() {
this.domItems = jQuery(React.findDOMNode(this.refs["items"]))
this.domItems.sortable({
stop: (event, ui) => {
// get the array of new index (http://api.jqueryui.com/sortable/#method-toArray)
const reorderedIndexes = this.domItems.sortable('toArray', {attribute: 'data-sortable'})
// cancel the sort so the DOM is untouched
this.domItems.sortable('cancel')
// Update the store and let React update (here, using Flux)
Actions.updateItems(Immutable.List(reorderedIndexes.map( idx => this.state.items.get(Number(idx)))))
}
})
}