Open beauchette opened 4 years ago
Same here
Oh that's a problem for me, because I update a db via https each time, so, even I filter out when it's the same content, there's always gonna be the problem of making 2 requests instead of one, the first time.
So I ended up using onEnd
but it's dirty, well, not the React way. I had to use custom attribute to set the id (as a dbid attribute) on the sortable elements, and then read them in the onEnd
function
Any update on this ? It actually fires when the drag starts, but also twice when the drag ends on the source list with only the last call being genuine. It make it quite painful to work with.
Thanks for the library
@Camsteack as I said in an earlier comment, setting state that often is not really a problem. What is a problem is to update your database through an http(s) request, so basically, I used the onEnd
event,
I had to add attributes to be able to get the database id from them, that's a little dirty but it worked
I ran across the same issue and I got around it by using state to track whether or not I was actively in the process of sorting. Here's a rough example:
import { ReactSortable } from 'react-sortablejs';
const Sorter = () => {
const [isSorting, setIsSorting] = useState(false);
const [items, setItems] = useState(['A', 'B', 'C', 'D', 'E', 'F']);
const updateOrder = (updatedList) => {
if (!isSorting) return;
setIsSorting(false);
setItems(updatedList);
};
return (
<ReactSortable
list={items}
onStart={() => setIsSorting(true)}
setList={(updatedList) => updateOrder(updatedList)}>
{items.map((item) => <div>{item}</div>)}
</ReactSortable>
);
};
export default Sorter;
Same here
Same here
I'm not sure why but @doug-stewart solution doesn't work for me. When isSorting is true, updateOrder fires with old updatedList, so by the time updateOrder fires with the correct sorted updatedList, isSorting is false and exits before updating the itemsState.
Instead I allow the updateOrder after onUpdate is fired, and I use a ref for isSorting, since this state is not meant to rerender the component but to know whether Sortable is sorting or not.
import { ReactSortable } from 'react-sortablejs';
const Sorter = () => {
const isSortingRef = useRef(false);
const [items, setItems] = useState(['A', 'B', 'C', 'D', 'E', 'F']);
const updateOrder = (updatedList) => {
if (!isSortingRef.current) return;
isSortingRef.current = false
setItems(updatedList);
};
return (
<ReactSortable
list={items}
onUpdate={() => isSortingRef.current = true}
setList={(updatedList) => updateOrder(updatedList)}>
{items.map((item) => <div>{item}</div>)}
</ReactSortable>
);
};
export default Sorter;
@aquaductape Hi,
I tried your way but for me onUpdate
is not working at all. Any hints on how to make it work?
before set state check equality of new and old state by lodash. this is example code for class component. in my case items has 2 fields: id & name.
updateOrder = (newState) => {
const prevState = this.state.list;
newState = newState.map(item => _.pick(item, ['id', 'name']));
if (_.isEqual(prevState, newState)) return;
this.setState({
list: newState,
})
}
This is the easiest drag and drop library to use in the react ecosystem, but instead of onEnd, it uses buggy setList, which is really annoying
Im just shocked such a great library still has this most fundamental of issues :(
import React, { FC, useState } from "react";
import { ReactSortable } from "react-sortablejs";
interface ItemType {
id: number;
name: string;
}
export const BasicFunction: FC = (props) => {
const [state, setState] = useState<ItemType[]>([
{ id: 1, name: "shrek" },
{ id: 2, name: "fiona" },
]);
// Use this custom function so that only update when changes on drag and drop
function updateState(newState) {
if (JSON.stringify(newState) !== JSON.stringify(state)) {
setState(newState);
}
}
return (
<ReactSortable list={state} setList={updateState}>
{state.map((item) => (
<div key={item.id}>{item.name}</div>
))}
</ReactSortable>
);
};
Working Solution :
import React, { FC, useState } from "react"; import { ReactSortable } from "react-sortablejs"; interface ItemType { id: number; name: string; } export const BasicFunction: FC = (props) => { const [state, setState] = useState<ItemType[]>([ { id: 1, name: "shrek" }, { id: 2, name: "fiona" }, ]); // Use this custom function so that only update when changes on drag and drop function updateState(newState) { if (JSON.stringify(newState) !== JSON.stringify(state)) { setState(newState); } } return ( <ReactSortable list={state} setList={updateState}> {state.map((item) => ( <div key={item.id}>{item.name}</div> ))} </ReactSortable> ); };
There is a bizarre attribute that you have to map out called chosen in newState. This setList is really perplexing how not good it is.
Describe the bug setList is supposed to help set the state. It is fired when drag ends, with the new state. Unfortunately it is also called when the Drag starts with the actual state, so basically, there is a setState for nothing at drag start.
To Reproduce
setStateWrapper
that just executessetState
and add a console.log with thesetState
parametersetStateWrapper
is called sometimes 3 timesExpected behavior setList being there to help state the state, should only fire once.
Information "react": "^16.9.0", "react-sortablejs": "^2.0.11",
though, when I created a new react app to test, it was: "react": "^16.13.1", "react-sortablejs": "^2.0.11",