- React Native By Example
- Richard Kho
- 658字
- 2021-07-09 18:21:35
Save button
In this section, we will create a button in the upper-right corner of the navigation bar that is labeled as Save. When it is tapped on, the following two things must happen:
- The changes the user made to the to-do item (such as its name, completion status, and due date) must be saved to AsyncStorage, overwriting its previous details
- The TasksList must be updated so that the user visually sees the changes they made right away
Rendering the Save button is easy with React Native. The object that gets pushed to NavigatorIOS needs to receive the following two key/value pairs:
- rightButtonTitle: This is a string that renders the text shown in that area
- onRightButtonPress: This is a callback that is fired when that button is pressed
At face value, this looks simple. However, we can't pass any information to the onRightButtonPress method of NavigatorIOS from a rendered child. Instead, we have to keep a copy of the changes we make inside our TasksList component as well, and update them as the DatePickerIOS, TextInput, and Switch components within EditTask are updated.
// Tasks/app/components/TasksList/index.js
...
export default class TasksList extends Component {
constructor (props) {
...
this.state = {
currentEditedTaskObject: undefined,
...
};
}
...
_completeTask (rowID) {
const singleUpdatedTask = {
...this.state.listOfTasks[rowID],
completed: !this.state.listOfTasks[rowID].completed
};
this._saveAndUpdateSelectedTask(singleUpdatedTask, rowID);
}
This is no longer an asynchronous function. The part of the function that took advantage of async/await is broken off into _saveAndUpdateSelectedTask.
Set the currently edited task object to state:
_editTask (rowData, rowID) {
this.setState({
currentEditedTaskObject: rowData
});
Add an onRightButtonPress callback and string for the right button's title:
this.props.navigator.push({
...
onRightButtonPress: () => this._saveCurrentEditedTask(rowID),
rightButtonTitle: 'Save',
Pass in four new functions to EditTask that deal with the item's details:
passProps: {
changeTaskCompletionStatus: (status) =>
this._updateCurrentEditedTaskObject('completed', status),
changeTaskDueDate: (date, formattedDate) =>
this._updateCurrentEditedTaskDueDate
(date, formattedDate),
changeTaskName: (name) =>
this._updateCurrentEditedTaskObject('text', name),
clearTaskDueDate: () =>
this._updateCurrentEditedTaskDueDate(undefined, undefined),
}
});
}
Add arguments for _editTask to accept:
_renderRowData (rowData, rowID) {
return (
<TasksListCell
...
onLongPress={ () => this._editTask(rowData, rowID) }
...
/>
)
}
This is the logic previously found in componentDidMount. It was broken into its own function since _saveCurrentEditedTask needs to call it:
async _saveAndUpdateSelectedTask (newTaskObject, rowID) {
const listOfTasks = this.state.listOfTasks.slice();
listOfTasks[rowID] = newTaskObject;
await AsyncStorage.setItem('listOfTasks',
JSON.stringify(listOfTasks));
this._updateList();
}
To save the current edited task, we pass the object and rowID to _saveAndUpdateSelectedtask, and then call pop on the navigator:
_saveCurrentEditedTask (rowID) {
this._saveAndUpdateSelectedTask(this.state.currentEditedTaskObject,
rowID);
this.props.navigator.pop();
}
This function updates the date and formattedDate of the current edited task object:
_updateCurrentEditedTaskDueDate (date, formattedDate) {
this._updateCurrentEditedTaskObject ('due', date);
this._updateCurrentEditedTaskObject ('formattedDate',
formattedDate);
}
The following function accepts a key and value, creates a clone of currentEditedTaskObject with the new value, and sets it in state:
_updateCurrentEditedTaskObject (key, value) {
let newTaskObject = Object.assign({},
this.state.currentEditedTaskObject);
newTaskObject[key] = value;
this.setState({
currentEditedTaskObject: newTaskObject
});
}
...
}
The last two functions' purpose is to update the TasksList local state copy of the object being edited. This is done for two reasons:
- Any updates we make to EditTask, such as changing the name, completion status, and due date, currently do not propagate up to its parent
- Additionally, we can't just point the values in EditTask to what gets passed in as props since the EditTask component does not rerender whenever the props being passed to it change
EditTask gets a couple of changes including new propTypes for the component to expect:
// Tasks/app/components/EditTask/index.js
...
export default class EditTask extends Component {
static propTypes = {
changeTaskCompletionStatus: PropTypes.func.isRequired,
changeTaskDueDate: PropTypes.func.isRequired,
changeTaskName: PropTypes.func.isRequired,
clearTaskDueDate: PropTypes.func.isRequired,
...
}
The changes that EditTask receives involve calling the functions that are passed to it as props to update the parent component's data for saving:
...
render () {
...
const dueDateSetTitle = 'Due On ' +
this.state.formattedDate || this.props.formattedDate;
...
}
_changeTextInputValue (text) {
...
this.props.changeTaskName(text);
}
_clearDate () {
...
this.props.clearTaskDueDate();
}
...
_onDateChange (date) {
...
this.props.changeTaskDueDate(date, formattedDate);
}
...
_onSwitchToggle (completed) {
...
this.props.changeTaskCompletionStatus(completed);
}
}
- 演進式架構(原書第2版)
- 案例式C語言程序設計
- Python for Secret Agents:Volume II
- PHP程序設計(慕課版)
- ASP.NET Core 2 and Vue.js
- jQuery開發基礎教程
- Learning JavaScript Data Structures and Algorithms
- Cybersecurity Attacks:Red Team Strategies
- App Inventor創意趣味編程進階
- INSTANT Silverlight 5 Animation
- Practical Microservices
- 一步一步跟我學Scratch3.0案例
- 多媒體技術及應用
- ABAQUS6.14中文版有限元分析與實例詳解
- Mastering Node.js