Add a Complete Toggle with React Native Switch

Jason Brown
InstructorJason Brown
Share this video with your friends

Social Share Links

Send Tweet
Published 8 years ago
Updated 6 years ago

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.

Mike
Mike
~ 8 years ago

Where you have the

<Row
                  key={key}
                  onComplete={(complete) => this.handleToggleComplete(key, complete)}
                  {...value}
/>

where does complete come from in your arrow function? How is it being passed back up? And for inside Row, even though complete is a prop why don't you call it like <Row complete={ }>?

Jason Brown
Jason Browninstructor
~ 8 years ago

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.

Mike
Mike
~ 8 years ago

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);
                }}
Jason Brown
Jason Browninstructor
~ 8 years ago

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)

Jason Brown
Jason Browninstructor
~ 8 years ago

If you are stuck, feel free to put your current code up on a public repository and I'm happy to review it.

Mike
Mike
~ 8 years ago

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

Jason Brown
Jason Browninstructor
~ 8 years ago

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 ListViews. 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.

Mike
Mike
~ 8 years ago

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.

Jason Brown
Jason Browninstructor
~ 8 years ago

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

Mike
Mike
~ 8 years ago

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

Jason Brown
Jason Browninstructor
~ 8 years ago

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

Jason Brown
Jason Browninstructor
~ 8 years ago

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.

Markdown supported.
Become a member to join the discussionEnroll Today