you're calling setState twice. doesn't this cause it to render twice as well? maybe a race is happening?
that.setState({updatePressed: true});
that.setState({pressTest: 2});
that.setState({pressTest: 2}); added to test. Maybe I should comment the first line.
There's no problem in calling setState() more than once, although if it wasn't a test it'd be better to put then together.
The issue there is that setState() is async, and you're trying to access it on the next line. It doesn't mean it's not working, it means it wasn't evaluated yet. It'll just be evaluated at the end of the frame, when all the calls have been finished.
https://facebook.github.io/react/docs/component-api.html#setstate
I know the setState can't immediately update.
my problem is that, I can't get the newly state at renderRow (gist)
this.state.pressTest (at line 98) always be 0 but not 2. At the same time, I can get a 2 at line 82.
Oh, I see, renderRow is never called again...
I had a look on the source, but I'm not sure what's the best solution here. /cc @vjeux
Try always returning true from rowHasChanged. Better solution is not to access this.state/this.props/this.context/any non-constant instance variable or global variable from renderRow, and instead pass data through only the data source. The docs should probably mention this as a rule of thumb since there have been a couple similar issues opened.
I tried the rowHasChanged trick, but it doesn't work. It's not called again either, since the row is already rendered and the data source hasn't change, it doesn't try to re-render.
I've tried to do the same thing as well some days ago and had no real luck. Some things a tried (which are kind of dirty though):
I'm currently living with the last option, although it has probably some disadvantages. Had no time to investigate it any further.
Good point, you need to force a data source update too. Option 3 of cloning a data source with the same content should provide the desired behavior.
For the API it would be easier if hasRowChanged were replaced with shouldComponentUpdate plus component pooling to avoid allocations. I haven't thought through the details but I think we want a React-style API instead of this UIKit-inspired one.
Although I remember that option 3 (creating a new DS) did not work as well in some scenarios as the rows were not re-rendered (debugged a little bit into it and noticed that even with a new DS, the re-render method was not executed). Thats why I finally began to use option 4. I remember discussing with someone about that topic over there @ freenode about 2 or 3 days ago, but can't remember anymore. My context was adding a new item in a gridview (https://github.com/lucholaf/react-native-grid-view) on the same row, which did not make RN to re-render the row.
I've also been having problems with this. For me, I was trying to render a list of notifications, each with a time associated. So, like like how tweets are labeled "2m ago".
I initially tried calculating the relative times inside renderRow, but that didn't work. So, I ended up cloning notifications (via JSON.parse(JSON.stringify(notification)). That worked, but would spuriously cause some of my rows to become blank whenever the row changed enough (going from "4s ago" to "5s ago" was fine, but going from "9s ago" to "10s ago" wasn't).
So, I then tried removing the time from my renderRow function, so it'd just render the notification. The time was still being updated in the background, it just wasn't being displayed. This, indeed, would make my rows more stable, and they no longer disappear randomly.
After reading @ide's suggestion above, then set put all my times into state, but still caused my rows to spuriously become blank. Once again, removing the times fixed the issue. I'm still not sure why this is. Have you encountered these problems, @PhilippKrone?
Edit: nevermind the blank text problem. @ide graciously debugged with me over IRC, and it was mentioned / fixed in #813. Thanks again, @ide!
clone the origin DS to force the renderRow render again works. Thanks a lot.
var _ds = JSON.parse(JSON.stringify(ds));
that.setState({
dataSource: that.state.dataSource.cloneWithRows(_ds),
updatePressed: true
});
@iahu could you share more details on this code? What is the context?
@kevinzzz007 source code is here https://gist.github.com/iahu/0e524f4612a8925f2f9c
@iahu How did you manage to force the ListView to re-render? I didn't see anywhere you created _ds...
OK, I have update my gist.
@iahu thanks! That made it really clear. So in my case, I got my data from ParseReact, here is my code:
var _ds = this.data.driver_available_times;
this.setState({
dataSource: this.state.dataSource.cloneWithRows(_ds),
});
You need to make a clone of you original datasource. That means it's must be a "immutable object".
So as your case, you can do that like me.
var _ds = JSON.parse(JSON.stringify(this.data.driver_available_times));
@iahu yeah, I tried that, an issue with that is my Parse object has date object in it, which if I run the object through
(JSON.stringify(this.data.driver_available_times));, some data will be lost(time zone, and also with these information lost, I can't run some function anymore), so I had to use
var _ds = this.data.driver_available_times;
@kevinzzz007 try this code
var d = this.data.driver_available_times;
var _ds = new Date(d); // d !== _ds, make a clone of your Date object.
@iahu thanks! I will give it a shot :-)
re-create the datasource by changing a dummy value (so that the rows are re-rendered).
this fixed this propblem well.
look detail at http://stackoverflow.com/questions/29933546/updating-this-state-datasource-doesnt-update-listview
this._dataSource[movie].imageurl="/dist/images/default_picture.jpg";
this.ds = new ListView.DataSource({rowHasChanged: (row1, row2) => row1 !== row2});
this.setState({
dataSource: this.ds.cloneWithRows(this._dataSource),
loaded: true,
});
@fiowind that makes sense, I might try this solution in the future, but I do think there should be an option for us to reload the ListView
ListView is designed to have all data for renderRow come from the data source. I recommend putting the selection state in the row data of the data source and updating that with clone and setState. Does that work?
Sounds like some documentation improvements would help...
this.state.todoItemArray = this.state.todoItemArray.concat({ 'Item': item, 'TodoItemstyle': todoItembgStyle });
this.setState({
todoItemArray: this.state.todoItemArray,
isDeleteDefaultItem: true
});
Use Concat.. it will work.. adding new list item to the datasource... Here TodoItemArray is my datasource..
Most helpful comment
ListView is designed to have all data for renderRow come from the data source. I recommend putting the selection state in the row data of the data source and updating that with clone and setState. Does that work?
Sounds like some documentation improvements would help...