AsyncStorage - error (React Native) - xcode

I have a problem with persisting user's data in a react-native application I am currently working on with my team. We have an app with a list of events and when you click on an event, it opens up the event details page and shows a menu to choose yes/no/maybe (attendance menu) which the user can toggle to select whether or not they are attending the event. Our code looks like this:
import React from 'react';
import { AsyncStorage, Text, View, StyleSheet, Picker } from 'react-native';
export class PickerMenu extends React.Component {
state = { choice: '' }
componentDidMount = () => AsyncStorage.getItem('choice').then((value) => this.setState({ 'choice': value }))
setName = (value) => {
AsyncStorage.setItem('choice', value);
this.setState({ 'choice': value });
}
render() {
return (
<View>
<Picker selectedValue = {this.state.choice} onValueChange = {this.setName} itemStyle = {styles.item}>
<Picker.Item label = "Yes" value = "yes" />
<Picker.Item label = "Maybe" value = "maybe" />
<Picker.Item label = "No" value = "no" />
</Picker>
</View>
);
}
}
We are running the build on Xcode. We want the user's choice to still be present after the page has been refreshed or the app has closed - this works HOWEVER,the choice that the user has selected, stays the same for every event. E.g. if they select 'Maybe' for event 1, the selection 'maybe' is highlighted for every other event in our app.
We had an idea of grabbing unique event IDs and implementing this into the AsyncStorage function but we are unsure of how to do this.
Any help will be great and much appreciated - thank-you!

In this case you need to use the async/await to store at the async storage
like this:
setName = async (value) => {
await AsyncStorage.setItem('choice', value);
this.setState({ 'choice': value });
}
Hope i have helped!
Some useful links for you:
https://facebook.github.io/react-native/docs/asyncstorage.html

Related

Force react-select to show all options using Elasticsearch

I'm trying to implement this with elasticsearch and it is working But how i can force this show results which can be different from the searched terms? For example i search ardino, The elasticsearch give me the word arduino but then react-select does not show that result because ardino does not contain arduino. I know the idea of this library is exactly that and it is working ok, but i have most of the things already implemented and it is only missing that part.
The handle is giving the right behavior and it is populating the options correctly.
Thank you
<Select
name= {this.state.selectedOption}
value={this.state.selectedOption}
isSearchable={true}
onChange = {(val) => {
this._handleSearch(val.value)
}}
onInputChange = {(val) => {
this._handleSearch(val)
}}
options={this.state.options}
/>
I would recommend using the Async component which can simplify your code. You will not need the onChange handler anymore.
isSearcheable is true by default, no need to specify.
In answer to your question: Make sure you are passing in label and value keys with each result option. If you want to customize any of the results, for example adding the search term in the results, you can manually manipulate the options array.
import React, {Component} from 'react';
import AsyncSelect from 'react-select/lib/Async';
class Search extends Component {
state = {inputValue: ""}
onInputChange = (inputValue) => {
this.setState({ inputValue });
};
getSearchResults = async (inputValue) => {
// elastic search results
let options = await fetch(`searchElastic/${inputValue}`);
// input value of drop-down
const inputValue = this.state.inputValue;
// manually add input field as an option in drop-down
if (inputValue.length > 0)
options.unshift({label: inputValue, value: inputValue})
// async handling of new props
return options.map(opt => {
return {label: opt.name, value: opt.value}
})
}
render() {
return <AsyncSelect
onInputChange={this.onInputChange}
loadOptions={this.getSearchResults}
/>
}
}
export default Search;

Complex navigation in React Native using react-navigation and Redux

I have the following navigation structure in my React Native app:
StackNavigator configured with 3 routes:
Splash screen (React Component)
StackNavigator for my login flow
DrawerNavigator for my core app screens.
The DrawerNavigator has some dynamic multiple routes, but also one static route which is another StackNavigator.
Everything seems to be working as expected:
The store is being updated accordingly.
Navigation between screen works.
Go back between screen works when configured within each component, with the following command:
this.props.navigation.goBack();
My question is - is there a way for me to handle back button on Android globally? Currently when I click on the back button, nothing happens (due to the fact I'm using Redux). Should I handle the back button in each component or is there a way of doing it using Redux?
A bit late, but there is a way to handle this with redux. In your index.js file where you create your store you can make export a class and add a componentWillMount call to handle dispatching a call to your redux actions. Just remember to import the actions you need above.
const store = configureStore();
export default class Index extends Component {
componentWillMount = () => {
BackHandler.addEventListener('hardwareBackPress', () => {
const { nav: { routes } } = store.getState();
const currentRouteName = routes[routes.length-1].routeName;
if (currentRouteName === 'EditCoupleProfile') {
store.dispatch(editCoupleActions.navigateBack())
} else if ( currentRouteName === 'EditInterests' ) {
store.dispatch(interestsActions.navigateBack())
} else {
store.dispatch(popFromStack());
}
return true;
})
};
componentWillUnmount = () => {
BackHandler.removeEventListener('hardwareBackPress');
};
render() {
return (
<Provider store={store}>
<AppWithNavigation />
</Provider>
);
}
}

react-redux together with components status

in a react UI I have a table component. You can edit one row of the table by clicking a edit button or you can add a new record by clicking a "new-record-button". When clicking the edit button an redux-action is triggered which takes the row and sets a visible property of a modal dialog. When the "new-record-button" is clicked an action is triggered which creates a new empty data item and the same modal dialog is triggered.
In the modal dialog I have several text components with onChange method.
in this onChange-method the data-item is written.
When to user clicks a save-button the edited dataItem is saved to the database.
So my code looks like:
const mapStateToProps = (state) => ({
dataItem: state.datItemToEdit || {},
...
});
...
handleTextChange(event) {
const {
dataItem
} = this.props;
const id = event.target.id;
const text = event.target.value;
switch (id) {
case 'carId': {
dataItem.carId = text;
break;
}
...
}
this.forceUpdate();
}
...
<TextField
...
onChange={event => this.handleTextChange(event)}
/>
I have several question regarding this approach. First I do not understand why in handleTextChange we can write to dataItem. It does work apparently.
dataItem.carId is set in the example code but I thought
const {dataItem} = this.props;
gives us a local read-only variable dataItem just to read from the props...
Next thing I think is a poor design. After reading in a book about react I think we should not write to props but only set a state.
In my example I get the the dataItem from the redux-state. The mapStateToProps maps it to the (read-only) props of the component, right?!. But I want to EDIT it. So I would have to copy it to the state of my component?
But where to do it?
Once in the state of my component I could simply call this.setState for the various text-fields and the component would render and I could abstain from forceUpdate(), right?!
Can someone explain how the redux status plays together with the component status and props for this example?
In redux or react, you shouldn't write to the props directly because you should keep your props as immutable. Redux forces us to use immutable state because state is a source of truth for the application. If the reference to state changes then only your app should render. If you'll mutate your state (objects) then the references don't get changed and your app doesn't know whether some state has been changed or not. React/Redux doesn't give you read-only objects automatically. You can mutate them anytime but as I told you, it can cause problems that Your app won't know when to re-render. If you want to have this read-only property inherently, you should probably use immutable.js
About your second question that you'll have to copy the props to the component's state and where you should do it. You should do it in the constructor of the component and you should use immutibility helper
import React from React;
import update from 'immutibility-helper';
class Modal extends React.Component {
constructor(props){
this.state = {
dataItem: dataItem,
};
}
...other methods
handleTextChange(event) {
const {
dataItem
} = this.props;
const id = event.target.id;
const text = event.target.value;
switch (id) {
case 'carId': {
this.props.updateItem(this.state.dataItem, text); //fire a redux action to update state in redux
this.setState(update(this.state, {
dataItem: {
carId: {$set: text},
}
});
break;
}
...
}
}
}
You wouldn't have to do forceUpdate in such case because the reference to state will change and the component will re-render itself.
Also, you can use forceUpdate in your application but personally I don't find it a great idea because when React/Redux is giving you the flow of state, by using forceUpdate, you're breaking the flow.
The last question is how redux and react state plays together. That is also a matter of choice. If I have a app level state, e.g., in your case you've some app level data, you should put that in your redux state and if you have a component level things, such as opening a modal or opening a third pane. That's the convention I follow but that can really depend on how you want to exploit react and redux state.
Also, in above code, I put the redux state in component state too (because you asked where to put that) but Ideally you should fire a redux action and update in redux state. In this way, you will restrict yourself from state duplication in react and redux.
import React from React;
import {updateItem} from './actions';
class Modal extends React.Component {
...other methods
handleTextChange(event) {
const {
dataItem
} = this.props;
const id = event.target.id;
const text = event.target.value;
switch (id) {
case 'carId': {
this.props.updateItem(this.props.dataItem, text); //fire a redux action to update state in redux
break;
}
...
}
}
}
const mapStateToProps = (state) => ({
dataItem: getDataItem(state), //get Data Item gets Data from redux state
});
export default connect(mapStateToProps, {updateItem: updateItem})(Modal);
in Actions:
updateItem = (dataItem, text) => dispatch => {
dispatch({type: 'UPDATE_ITEM', payLoad: {dataItem, text});
};
in Reducer:
export default (state = {}, action) => {
switch(action){
case 'UPDATE_ITEM': {
return {
...state,
dataItem: {
...action.dataItem,
carId: action.text,
}
};
}
}
}
In this way, your state will be pure and you don't have to worry about immutibility.
EDIT:
As constructor will be called only once, you should probably use componentWillReceiveProps so that whenever you render the component, you get the next updated props of the component. You can check whether the carId of dataItem is same or not and then update the state.
componentWillReceiveProps(nextProps){
if(nextProps.dataItem.carId !== this.props.dataItem.carId){
this.setState({dataItem: nextProps.dataItem});
}
}
You should only use redux when you want different, unrelated components in your app to know and share the specific state.
e.g. - When a user logs in to your app, you might want all components to know that user so you'll connect your different containers to the user reducer and then propagate the user to the components.
Sounds like in this case you have a classic use case for using the inner state.
You can use the parent of all TextFields to maintain all rows, edit them by index, etc.
Once you start using redux, it's really easy to make the mistake of transferring the entire state of the components to the reducers, I've been there and stopped doing it a while ago :)

Nativescript Switch prevent change event firing on initial binding

Hi my template is something like the below
<ListView [items]="modules">
<template let-item="item" >
<StackLayout orientation="vertical">
<Switch (checkedChange)="onSwitchModule(item.id,$event)" [checked]="item.active"></Switch>
</StackLayout>
</template>
</ListView>
My controller is
ngOnInit() {
this._moduleService.getUserModules()
.subscribe(
response=>{
this.modules = response.data;
}
)
}
onSwitchModule(itemId) {
console.log(itemID); //Gets called on initial true binding on switch checked
}
The onSwitchModule get called everytime the page loads with item.active is true on any item, how to handle this ?
NOTE: Beginner in Nativescript
What I did to overcome this is I watch for tap events instead of checkedChange:
<Switch (tap)="switchClicked" [checked]="item.active"></Switch>
and in the callback, you can get the current item from bindingContext:
function switchClicked(args) {
const item = args.object.bindingContext.item;
}
I ran into a similar issue: loading up settings data from an API, and having the checked event fire for the value I'd set from the api -- not desirable in my case. I didn't see a great way to prevent events from firing on the initial binding, so I decided to simply ignore events until I knew they were legit events from the user actually using the switch.
I did that by using a property switchReady to keep track of when you want to start recognizing change events. This pattern also keeps the toggle disabled until you're ready to start accepting changes. This makes use of Switch's isEnabled property, see docs here.
Markup
<Switch [checked]="currentSettings.pushTurnedOn" [isEnabled]="switchReady" (checkedChange)="onPushSettingChange($event)" row="0" col="1"></Switch>
Component
export class SettingsComponent implements OnInit {
currentSettings: Settings = new Settings(false)
switchReady: boolean = false
ngOnInit() {
this.getCurrentSettings()
}
public onPushSettingChange(args) {
let settingSwitch = <Switch>args.object
if (settingSwitch.isEnabled) {
// do something with the event/change
} else {
// we aren't ready to accept changes, do nothing with this change
return
}
}
getCurrentSettings() {
this.settingsService.loadCurrentSettings().subscribe(
() => {
this.currentSettings = this.settingsService.currentSettings
// we've applied our api change via data binding, it's okay to accept switch events now
this.switchReady = true
},
err => alert('There was a problem retrieving your settings.')
)
}
}

Re-rendering a single row of a list without re-rendering the entire list

we're trying to implement a contact list that works just like the new Material Design Google Contacts (you must enable the material design skin to see it) using material-ui.
Specifically we're trying to show a checkbox instead of the avatar on row hover.
We'd like to catch and re-render only the interested row (when hovered) and show the avatar/checkbox accordingly... this seems an easy task but we're not able to isolate the render to the hovered row (instead of re-rendering the entire list)
Do you have any suggestion on how to do something like this?
Our temporary solution uses a container component that handles the table:
When a row is hovered we capture it from onRowHover of the Table component and save it in the container state. This triggers a re-render of the entire list with really poor perfomance.
You can see a video of the issue here.
Here is a code sample:
import React from 'react'
import Avatar from 'material-ui/lib/avatar'
import Checkbox from 'material-ui/lib/checkbox'
import Table from 'material-ui/lib/table/table'
import TableHeaderColumn from 'material-ui/lib/table/table-header-column'
import TableRow from 'material-ui/lib/table/table-row'
import TableHeader from 'material-ui/lib/table/table-header'
import TableRowColumn from 'material-ui/lib/table/table-row-column'
import TableBody from 'material-ui/lib/table/table-body'
import R from 'ramda'
export default class ContactsList extends React.Component {
constructor (props) {
super(props)
this.state = { hoveredRow: 0 }
this.contacts = require('json!../../public/contacts.json').map((e) => e.user) // Our contact list array
}
_handleRowHover = (hoveredRow) => this.setState({ hoveredRow })
_renderTableRow = ({ hovered, username, email, picture }) => {
const checkBox = <Checkbox style={{ marginLeft: 8 }} />
const avatar = <Avatar src={picture} />
return (
<TableRow key={username}>
<TableRowColumn style={{ width: 24 }}>
{hovered ? checkBox : avatar}
</TableRowColumn>
<TableRowColumn>{username}</TableRowColumn>
<TableRowColumn>{email}</TableRowColumn>
</TableRow>
)
}
render = () =>
<Table
height='800px'
fixedHeader
multiSelectable
onRowHover={this._handleRowHover}
>
<TableHeader displaySelectAll enableSelectAll>
<TableRow>
<TableHeaderColumn>Nome</TableHeaderColumn>
<TableHeaderColumn>Email</TableHeaderColumn>
<TableHeaderColumn>Telefono</TableHeaderColumn>
</TableRow>
</TableHeader>
<TableBody displayRowCheckbox={false} showRowHover>
{this.contacts.map((contact, index) => this._renderTableRow({
hovered: index === this.state.hoveredRow,
...contact }))
}
</TableBody>
</Table>
}
Thank you in advance.
You could wrap your rows into a new component implementing shouldComponentUpdate like so :
class ContactRow extends Component {
shouldComponentUpdate(nextProps) {
return this.props.hovered !== nextProps.hovered || ...; // check all props here
}
render() {
const { username, email, ...otherProps } = this.props;
return (
<TableRow { ...otherProps } >
<TableRowColumn style={{ width: 24 }}>
{this.props.hovered ? checkBox : avatar}
</TableRowColumn>
<TableRowColumn>{this.props.username}</TableRowColumn>
<TableRowColumn>{this.props.email}</TableRowColumn>
</TableRow>
);
}
}
Then you can use it in your ContactList component like so :
this.contacts.map((contact, index) => <ContactRow key={contact.username} {...contact} hovered={index === this.state.hoveredRow} />)
If you don't want to manually implement shouldComponentUpdate, you can use React's PureRenderMixin or check a lib like recompose which provides useful helpers like pure to do so.
EDIT
As pointed out by the OP and #Denis, the approach above doesn't play well with some features of the Table component. Specifically, TableBody does some manipulation on its children's children. A better approach would be to define your ContactRow component like so:
class ContactRow extends Component {
shouldComponentUpdate(nextProps) {
// do your custom checks here
return true;
}
render() {
const { username, email, ...otherProps } = this.props;
return <TableRow { ...otherProps } />;
}
}
and then to use it like this
<ContactRow { ...myProps }>
<TableRowColumn>...</TableRowColumn>
</ContactRow>
But I guess having TableRow re-render only when necessary is a feature everyone would benefit from, so maybe a PR would be in order :)

Resources