Saving input data on async storage - reactjs

I have been trying to save input data on async storage but whenever i go back after input, it shows the previous data, not the updated one. Here's the code where i think some changes are needed.
constructor(props) {
super(props)
this.state = {
userData: {
address: ''
}
}
}
saveAddressEdit = (fieldName, fieldValue) => {
AsyncStorage.setItem(userData.address, fieldValue)
}
render() {
const {address} = this.state.userData
return (
<Input placeholder=" Type your Address:"
onChangeText={(address) => this.setState({address})}
value={address} />
<Button full danger onPress={()=>this.saveAddressEdit('address', address)} style={{backgroundColor:'#ff0000'}}>
);
}

If you want to persist you address through the component lifecycle using AsyncStorage then you should first save it properly and once you get back on the screen, fetch it and populate back it into your state, as component's state needs to be reinitialised using your AsyncStorage.
The following code should help initialize the address back to the stored one:
componentDidMount () {
AsyncStorage.getItem('address').then(value => {
if (value) {
this.setState({address: value});
}
})
}
and the below specifies the correct way to set the data to AsyncStorage:
saveAddressEdit = async (fieldName, fieldValue) => {
await AsyncStorage.setItem(fieldName, fieldValue)
}
OR you can also you the promise way just like I did in getItem above like setItem(fieldName, fieldValue).then(value => console.log('saved')) but since we don't need to handle anything in the then so I resist using promise in such places.

Related

Best way to display data form an api call

I was wondering what is the best way to display data from a rest api.
Actually what I do:
Call fetch function from componentDidMount();
setState to store my response
Check with ternary when render() if the value is set or not
it look like this :
(getcall() is a fetch function) :
async componentDidMount() {
const response= await getCall(
`event?ref=23876186`, // this is just to illustrate
);
this.setState({ payload: response})
}
Then in render() I do some :
{this.state.payload ? (
<h1>{this.state.payload.event.name}</h1>) : ( <h1></h1>)}
I though about calling my fetch function from the constructor, but it's weird to call an async function in a constructor, you loose the aync purpose.
I imagine some case like for an input :
<Input
type="text"
name="name"
id="name"
**value={this.state.event.name}**
placeholder="Name..."
onChange={this.handleName}
required
/>
If I want to set a value for it, like this.state.event.name, if I have 10 fields, I will have 10*2 times this kind of code because for each one I wrote a ternary.
So what is the best way to display data from an api call ?
Thanks for your answers
Instead of adding a lot of ternaries where you check if payload is set, you can return null early from the render method if it is not set yet.
Example
class App extends React.Component {
state = {
payload: null
};
async componentDidMount() {
const response = await getCall("/event?ref=23876186");
this.setState({ payload: response });
}
render() {
const { payload } = this.state;
if (payload === null) {
return null;
}
return <div>{/* ... */}</div>;
}
}

function returning data but not showing

I have this component
const SummaryBar = props => {
const { MainObject} = props;
const localGetUserFromID = userID => {
getEmailFromId(userID).then(results => {
return results.data.Title; //Comment: This one returning friendly name
});
};
return (<span>Hello {localGetUserFromID(MainObject.AuthorId)}</span>)
}
but when I render it somehow the its only showing Hello and not the output I am getting from my localGetUserFromID function. Am I doing wrong? Note the AuthorId is being pass to an API and the MainObject came from the App Level,
FYI when I try to debug it using dev tools the function is retuning the text I am look for.
localGetUserFromID() returns nothing, that is, undefined, and that's why you see Hello only.
And because localGetUserFromID() makes an asynchronous call to get an email from user ID, it doesn't have to be in render() method. Now this component is defined as a state-less component, but you can re-define it as a stateful component, call the getEmailFromId() in componentDidMount() life-cycle method, and use a return value as an internal state.
Then you can show a value of the internal state after Hello.
class SummaryBar extends Component {
// Skipping prop type definition.
constructor(props) {
super(props)
this.state = {
username: '',
}
}
componentDidMount() {
const { MainObject: { AuthorId } } = this.props
getEmailFromId(AuthorId).then((results) => {
this.setState({
username: results.data.title,
})
})
}
render() {
const { username } = this.state
return (
<span>
Hello { username }
</span>
)
}
}
When things run when debugging but not when running and you are using promises as you are, the 99% of the times is because promises hasn't been resolved when you print.
localGetUserFromID indeed returns a promise that resolves to the friendly name.
You can just prepend await to localGetUserFromID(MainObject.AuthorId) and rewrite you return as this:
return (<span>Hello {await localGetUserFromID(MainObject.AuthorId)}</span>)

Reactjs Select v2 - How to handle Ajax Typing?

I am using reactjs select 2 but I don't know how to make it work so that when a user types something in a ajax request is made and the results are sent back.
I see it has some async options but I don't get how it works and how I would get it to work with axios.
I come up with this but it is kinda laggy when a user types(probably because it is re-rendering it after each type) and when the user selects a choice the value disappears.
export default class TestComponent extends Component {
constructor(props) {
super(props);
this.state = {value: ""};
}
onInputChange(option) {
this.getOptionsAsync(option)
}
getOptionsAsync(newInput) {
var that = this;
console.log("ffd", newInput)
axios.get(`https://localhost:44343/api/States/GetStatesByText?text=${newInput}`)
.then(function (response) {
var formatedResults = response.data.map((x)=> {
return {value: x.id, label: x.name}
})
that.setState({
options: formatedResults,
value: newInput
})
})
.catch(function (error) {
});
}
render() {
console.log(this.state.value, "value")
return (
<div className="test">
<Select
onInputChange={this.onInputChange.bind(this)}
value={this.state.value}
options={this.state.options }
/>
</div>
);
}
}
You're going to be doing an api call every single time that you type a letter with the current way you're doing things. I would recommend just loading the states once at the beginning, perhaps in your ComponentDidMount() method.
If you pass the isSearchable prop to React-Select it will automatically work as a filter anyways.
Another thing I've had to do in this case which I believe will fix your change problem is to make sure it calls the handler on change not just on input change.
Pass this prop:
<Select
value={this.state.value}
options={this.state.options }
onChange={value => {
if (value) this.onInputChange(value)
else this.onInputChange('')
}
/>
Due to the way this is automatically bound to arrow functions, you won't have to bind to this if you change your onInputChange to the following:
onInputChange = (value) => {
this.getOptionsAsync(value)
}
Finally, you should be setting the state in the above function so the value is stored.
onInputChange = (value) => {
this.getOptionsAsync(value)
this.setState({value})
}

Hold the component's rendering until the store has finished hydration

I'm fetching my initial data like so:
export function populate (dispatch) {
let posts = []
dispatch(requestNews())
fetch(someEndPoint)
.then(payload => payload.json())
.then(items => {
//some fetching logic that populates posts list above
})
.then(() => { dispatch(receiveNews(posts)) })
.catch(err => {
console.log(err)
})
}
function request () {
return {
type: REQUEST_NEWS,
payload: {
populating: true
}
}
}
function receive (posts) {
return {
type: RECEIVE_NEWS,
payload: {
populating: false,
posts
}
}
}
As you can see above I'm setting the store with a field called populating which starts as false and changes to true when the 'request' is dispatched and then back to false when 'received' is dispatched.
Then my component looks something like the following:
import { populateNews } from '../modules/news'
class News extends React.Component {
constructor (props) {
super(props)
//this.mapPosts = this.mapPosts.bind(this)
}
componentWillMount () {
populateNews(this.props.dispatch)
}
render () {
if (!this.props.news.populating) {
return (
<div>
{this.props.news.posts[0].title}
</div>
)
} else {
return (
<div>loading</div>
)
}
}
}
On initial load render is being called before the store is populated with the fetched posts even though my populate switch changes back and forth as expected.
I've tried dealing with it using a local state on the component, so it's constructor has: this.state = {populating: false} and then the action creator changes that, but got the same result.
So at the moment my solution is to instead check if the state has a slice called 'posts' which is being created after the content is fetched and if it does to render it. like so:
render () {
return (
<div>
{this.props.news.posts ? <div>{this.props.news.posts[0].title}</div> : null }
</div>
)
}
This of course just renders the component and then renders it again after the store is updated with the posts, and is not an optimal solution like waiting with the render until the fetch is completed and the store is populated.
There's a long discussion about it here:
https://github.com/reactjs/react-redux/issues/210
How can I delay or better put condition the render itself?
Hmm, you might want to change your if statement. Unless your store is initializing populating to true, it will be undefined on the initial load and will pass your if (!this.props.news.populating) validation which will try to render the post title. Change your condition to look for a truthy value rather than a falsey value and you should have more control over it.

How to know if all the setState updates have been applied to the state in a React component?

I was reading the documentation about React setState, which says:
setState() does not immediately mutate this.state but creates a pending state transition. Accessing this.state after calling this method can potentially return the existing value.
Now I have a component like this:
class NoteScreenComponent extends React.Component {
constructor() {
super();
this.state = { note: Note.newNote() }
}
componentWillMount() {
this.setState({ note: this.props.note });
}
noteComponent_change = (propName, propValue) => {
this.setState((prevState, props) => {
let note = Object.assign({}, prevState.note);
note[propName] = propValue;
return { note: note }
});
}
title_changeText = (text) => {
this.noteComponent_change('title', text);
}
body_changeText = (text) => {
this.noteComponent_change('body', text);
}
saveNoteButton_press = () => {
// Save note to SQL database
Note.save(this.state.note)
}
render() {
return (
<View>
<TextInput value={this.state.note.title} onChangeText={this.title_changeText} />
<TextInput value={this.state.note.body} onChangeText={this.body_changeText} />
<Button title="Save note" onPress={this.saveNoteButton_press} />
</View>
);
}
}
What I'm wondering is, since setState does not update the state immediately, how can I know if the note I'm saving in saveNoteButton_press is the current version of the state? Is there some callback or something that I could poll to know if state has been fully updated?
What they are warning against is trying to do something in the same event loop.
method = () => {
this.setState({ note: 'A' })
saveNote(this.state.note) // <-- this.state.note will not have been updated yet.
}
or to setState using previous state:
method = () => {
let note = this.state.note // possible that `this.state.note` is scheduled to change
this.setState({ note: note + 'B' })
}
Since your user is going to be pushing the button after the setState scheduling, the state will have already been updated.
..but for theory's sake, let's imagine that somehow the input event and button happen in the exact same moment.. what would be the correct solution? If it was a single function call you probably wouldn't be using the new state since you already have the new note and the previous state.
method = (text) => {
let noteToSave = this.state.note + text // old state + new value
saveNote(noteToSave) // maybe this will fail
.then(response => this.setState({ note: noteToSave }))
.catch(err => this.setState({ error: 'something went wrong' }))
// optimistically update the ui
this.setState({ note: noteToSave })
}
but probably the most likely solution is to just pass what you want as an argument where you use it, rather than trying to access state which might be in a race condition, since render will happen after any state transitions.
method = (note) => {
noteToSave(note)
}
render() {
return (
<Button onPress={() => this.method(this.state.note)} /> <-- must be up to date here
)
}

Resources