I suspect the problem is the fact that (according to what you say) you are creating the DataSource in your render
method. You should create the ListView.DataSource
object in your constructor (or componentWillMount
), then call cloneWithRows
when your data changes, not in render
. The issue is that by recreating a new DataSource on each render, it never calls the rowHasChanged
function because there is never a previous state in the datasource.
Example Correct Implementation
In the below example, I setup the datasource in the constructor and store it in the state. Then once mounted, I have it load the todos, and update the datasource in the state, which will trigger a re-render.
Then, when you want to move a todo to the bottom, you would call this.moveItemToBottom(id)
, which modifies the state, and updates the datasource on the state, and re-render after setting up the LayoutAnimation.
class TodoList extends Component {
constructor(props, context) {
super(props, context);
this.state = {
ds: new ListView.DataSource({
rowHasChanged: (r1, r2) => (r1 !== r2),
}),
};
}
componentDidMount() {
loadInitialData();
}
loadInitialData() {
// Do something to get the initial list data
let todos = someFuncThatLoadsTodos();
this.setState({
ds: this.state.ds.cloneWithRows(todos),
});
}
moveItemToBottom(id) {
// get your state somewhere??
let todos = state.todoLists[list].todos;
let {todo, i} = forID(state.todoLists[list].todos, id)
todos.splice(i, 1).push({...todo, done: !todo.done});
LayoutAnimation.easeInEaseOut();
this.setState({
ds: this.state.ds.cloneWithRows(todos),
});
}
render() {
return (
<ListView
dataSource={this.ds}
// Other props
/>
);
}
}
EDIT/NOTE: My example doesn't take into account anything to do with MobX. I haven't used it, but from a cursory look, you may need to observe the todos list and update the datasource whenever it updates, and just have the moveItemToBottom
method update the MobX state and rely on the observable to setState
with the cloned datasource.