We'll add a cross platform Switch component to toggle the completeness of each todo item. We'll show how to pass down functions from the parent application to the child row of a ListView.
[00:00] Start by importing Switch from react-native. We'll then render our switch and pass in value={this.props.complete}. Now, when we refresh, we add an item, "This is a todo," to the list, we can see now there is a switch item being rendered, and we can toggle that with our toggle all complete.
[00:24] Our text is now slammed to the end, we'll wrap our text in a view, we'll then create a textWrap style. We'll give it flex:1, as well as marginHorizontal of 10, to add 10 pixels of margin on both sides.
[00:47] Then apply that, style={styles.textWrap}. We refresh, add an item, "this is a todo." We'll see now the text is back to where it started. Now, we'll go add a complete style, which we'll apply to our text when the item becomes complete.
[01:09] We'll say textDecorationLine, we'll give a value of line-through. We'll then go destruct our complet from this.props, we'll change this to just complete. We'll switch over our styles on our text to an array, and say complete && styles.complete. This will apply the styles.complete style when the thing becomes complete.
[01:35] When we refresh and add an item, and then toggle our completion status, we can see that the text becomes struck through. We'll now add an onValueChange function to our switch, and say {this.props.onComplete}.
[01:55] We'll go to our app, we'll create a handleToggleComplete, which will take a key and a completion status, then, we'll say const newItems = this.state.items, and we'll map over each of our items.
[02:19] Inside here, if we say if item.key doesn't equal the key that we're looking for, then, we'll just return the old item, otherwise, we'll return item that's read in, and then add the new complete flag. Then, we can say this.setSource(newItems, newItems).
[02:37] Now, we'll go bind our handleToggleComplete. We'll now pass in onComplete equals a function this.handleToggleComplete, which will pass in our key, and the complete value that gets passed back up to us. Now when we refresh, add an item, you can toggle the completion status at the row level.
It's being passed back up from Row.
How it works is we are passing a prop down to start <Row complete={false} />
.
This is then passed to the <Switch value={this.props.complete} />
component. Which Switch
is a native UI component.
We then additionally pass the onComplete
function that is passed to the Switch
like <Switch onValueChange={this.props.onComplete} />
When the user toggles the Native component, the onValueChange
function gets called which in turn calls our this.props.onComplete
function with either true
or false
. So the Native (iOS/Android) world is actually toggling the value we give it from false
=> true
and or true
=> false
.
We can't call it like <Row complete={ }>
because complete
is just a boolean value. We need to pass down a way to get notified when the user presses the switch.
Alternatively we could have passed down an onToggle
function and not received input from below. Something like
<Row
onToggle={() => this.handleToggleComplete(key, !value.complete)}
{...value}
/>
However in these cases I prefer to let the Native component provide me information directly.
The spread operator (the ...
) may be confusing but what that's doing is taking everything from the value
object and turning it into props.
So equivalent would be
<Row
key={key}
onComplete={(complete) => this.handleToggleComplete(key, complete)}
complete={value.complete}
title={value.title}
editing={value.editing}
/>
Hopefully this answers all of your questions. If you have more let me know.
Would I consider editing
to be a synthetic event? Also, (this is for your ToggleEdit lecture later) I tried calling .type
on editing
and it printed out undefined in the console, why is that? Here's what I had:
onToggleEdit={(editing) => {
console.log(editing.type);
this.handleToggleEditing(key, editing);
}}
The editing
that is being passed back us is just a boolean. Meaning true
or false
.
If you want log the value just log console.log(editing)
If you are stuck, feel free to put your current code up on a public repository and I'm happy to review it.
Here's my code:
https://gist.github.com/huyanhh/930a6c8a02874ded1adc9669cce65b72
I'm trying to extend upon what this lecture series and build a nested ListView. What it's supposed to look like is a custom ListView
of "pages" with each page containing a ListView
. I'm a little unsure on where I should be passing the props and where I should be managing state since I have to manipulate the dataSource
of nested lists. For now, I'm trying to pass up a Page
object back to the outer ListView
and then set the state up there. Unfortunately the page isn't rendering because I don't have my data sources set up correctly. Should I try managing my state only in app.js
? Or should it be delegated to each page within my outer list? I also don't know how to use the key
of each Row
in regards to updating the outer list if I'm not supposed to hold state
in my Page
component.
Thanks
You may not want to complicate things with a double ListView as this would require double data source syncing.
You may want to consider a ScrollView
of ListView
s. However it depends on how much data you're displaying.
This all sounds very complicated in terms of data management. In cases like these it can be easier to externalize data (in something like redux) and then you use componentWillReceiveProps
to update the DataSource
with your new data.
You can hold your state in your upper Page
component.
Do you have an example of your data structure? That may be easier for me to help you.
I want to display something that looks like the diary entries of MyFitnessPal. Each day there would be another item appended, and I want to display that list item as a page with its own list. I was thinking it would be too much data to have everything contained in a scroll view. The data structure looks like
{
day1:
{
pageID,
foodEntry[],
total,
}
day2:
{
pageID,
foodEntry[],
total,
}
...
}
where each object should be displayed in a separate screen.
Hey sorry for the late reply. What I would actually recommend is upgrading to React Native .43.
React Native .43 comes with a new FlatList
which gets rid of DataSource
junk.
You can read more about it here http://facebook.github.io/react-native/blog/2017/03/13/better-list-views.html
That sounds great! Would you still recommend nesting two FlatLists within each other then using redux to externally manage the data? I came across this issue in the repo today (it's an issue concerning list views and scroll views, and I believe that performance won't be too much of an issue with the new API because most of the content will be offscreen anyway but I wanted to get your opinion on it): https://github.com/facebook/react-native/issues/13038
Nesting listviews can be tricky but with the new FlatList
and SectionList
I think everything should be great.
With the new FlatList
it's recommended you hold either the data up top and pass it down, or in redux.
The reason is if you keep it inside the FlatList
items that get rendered, the row items will be unmounted.
So externally in redux is fine OR somewhere above all your lists and just pass it into a FlatList
then pass the other data into another FlatList
. Just don't hold any state inside the Row Items
I will also try and find some time to do a quick screencast showing off the FlatLists and SectionLists, and will try and nest them to see how they play together.
Where you have the
where does
complete
come from in your arrow function? How is it being passed back up? And for insideRow
, even thoughcomplete
is a prop why don't you call it like<Row complete={ }>
?