I new to react native, I trying to set AsyncStorage.getItem and AsyncStorage.setItem to save my state values,I setup the AsyncStorage.setItem in componentWillMount() function, in the first time my app is running I have state that my component use and he have some keys with specific values, if I setup the AsyncStorage.setItem to get the last item from my state, my component fails.
constructor(props) {
super(props);
this.state = { userAnswer: '', count: 0};
}
componentWillMount(){
AsyncStorage.getItem("userAnswer").then((value) => {
this.setState({userAnswer: value})}).done();
AsyncStorage.getItem("count").then((value) => {
this.setState({count: value})}).done();
}
saveData(){
AsyncStorage.setItem("userAnswer",this.state.userAnswer);
AsyncStorage.setItem("count",this.state.count);
};
I need that in the fist time the app is running the state should be the same meaning the count should stay 0, in my error the count appear as null
every help really appreciated! thanks.
Change it to this:
AsyncStorage.getItem("count").then((value) => {
this.setState({count: value || 0})}).done();
}
Related
I try to reset react component state by using preserved initial variable but this is not worked. What is the problem here?
First have I created a base state variable "baseState" then I will used to reset react state. but it not worked,
Here I create a sample program below,
class Product extends Component {
constructor(props){
super(props);
this.baseState = {isLoggedIn: true, loader:false, inputs : { name :null, category_id:null }};
this.state=this.baseState;
}
render {
(<Form>{/* form inputs */}</Form>)
}
// this is a onChange select event for update input values.
selectInputChange(e){
let stateInputs=this.state.inputs;
stateInputs[e.target.name]=e.target.value;
let inputErrors=this.state.errors;
let error=this.validateFormInput(e.target.name,e.target.value);
inputErrors[e.target.name]=error?error:null;
this.setState( prev =>{
return{
...prev, inputs: stateInputs, errors:inputErrors
}
});
console.log(this.baseState); // here value is updated.
}
handleFormSubmit(){
// after insert record through axios. then i try to reset like
this.setState(this.baseState);
// but state is not updated here.
}
}
export default withRouter(Product);
I am beginner to reactjs so help me to solve the problem, thank you!
You're making this.state point to the same object as this.baseState, so a change to one is a change the other because there is no difference: they're literally the same object. As you're discovering, this.setState(this.baseState) will do nothing, because you're updating the current state with itself, and is the same as saying this.setState(this.state).
If you want a state that you can truly reset to, use a function that generates a new object each time you call it:
class Product extends Component {
constructor(props){
super(props);
this.state = this.getInitialState();
}
getInitialState() {
return {
isLoggedIn: true,
loader: false,
inputs: {
name: null,
category_id: null
}
};
}
...
And then whenever you want to reset your component, you can call this.setState(this.getInitialState()), which will reset all the properties that are relevant to your initial state (while leaving anything else entirely untouched - you won't be removing any state values not covered by the initial state object).
I have something like:
const higherOrderComponent = (WrappedComponent) => {
return class extends React.Component {
render() {
<CustomProvider
render={({isLoading, playNote, playNoteAtTime, stopNote}) => {
return <WrappedComponent />;
}
/>
}
};
I have a single redux store higher up in my app and when the user interacts with 'WrappedComponent', it dispatches an action to the store, which causes 'higherOrderComponent' to re-render.
This hasn't been a problem before, as I have been favoring stateless components, and letting the new state wash through the full app on state change worked with no performance issues.
The Custom provider is asynchronously loading some files though, so now there is a noticable issue.
An idea for a solution is to introduce state in the component at the 'higherOrderComponent' level. When the component is first loaded, I will populate the state from the store props.
Then I could save up and dispatch actions to update the store later at an opportune time.
I'm not sure if I have created an anti-pattern with the above though (I'm using a Higher Order Component with 'Render props' at the same time).
Or if anyone can recommend a better solution?
edit
I was asked to show what CustomProvider is doing so I have added part of it here:
class SampleProvider extends React.Component {
static propTypes = {
instrumentNames: PropTypes.array.isRequired,
audioContext: PropTypes.instanceOf(window.AudioContext),
render: PropTypes.func
};
static defaultProps = {
currentInstrument: 'mooga'
};
constructor(props) {
super(props);
this.playNote = this.playNote.bind(this);
this.state = {
activeAudioNodes: {},
instrument: null
};
}
componentDidMount() {
sampleLoader.init(this.props.audioContext, sampleFilePaths).then(loadedSamplesBuffers => {
this.samplesBuffers = loadedSamplesBuffers;
console.log('End: loading samples');
});
}
playNote = midiNumber => {
let play = samplePlayer(this.props.audioContext, this.props.mainOutput, this.samplesBuffers);
play(midiNumber, 0, null, null, this.props.currentInstrument);
};
I think I'm missing a concept here about React and Redux. I'm trying to work with objects stored in redux, and I'm having trouble.
REDUX:
I have an action fetchItems, that gets all items from the database. This action works successfully.
REACT:
I have a container, UserProfile, that calls fetchItems in componentDidMount.
class UserProfile extends Component {
componentWillMount() {
console.log('------------ USER PROFILE -------------------');
}
componentDidMount() {
console.log('[ComponentDidMount]: Items: ', this.props.items);
this.props.fetchItems();
}
render() {
let profile = null;
console.log('[Render]: Items: ', this.props.items);
return <Auxillary>{profile}</Auxillary>;
}
}
const mapStateToProps = state => {
return {
items: state.items.items
};
};
const mapDispatchToProps = dispatch => {
return {
fetchItems: () => dispatch(actions.fetchItems())
};
};
export default connect(mapStateToProps, mapDispatchToProps)(UserProfile);
The problem I'm seeing is that this.props.items is always null (even though fetchItems is successful). The only way I can detect that items were stored in redux store is if I use componentWillRecieveProps(nextProps). Here, I successfully see the items in nextProps. I feel like using componentWillReceiveProps might be too "messy" though. I guess what I'm asking is, what is the standard way of dealing with updates to redux states in react?
Aseel
The cycle will be :
constructor()
componentWillMount() (will be soon deprecated by the way : https://medium.com/#baphemot/whats-new-in-react-16-3-d2c9b7b6193b)
render() => first render (this.props.items, coming from mapStateToProps will be undefined)
componentDidMount() => launching fetchItems() => changing redux state => changing the this.props.items => launching the second render() where this.props.items will be set.
So :
you should have two console.log('[Render]: Items: ', this.props.items);
you should deal with a "loading" state when the this.props.items is null
If the second console.log is still null, Try to add log in your reducer, in the mapStateToProps, ... perhaps it's not state.items.items ...
In react, we have something called state. if the state of a component is changed the component will re-render. Having said that we can use this.setState() inside componentWillRecieveProps to update the state which in turn will rerender the component. So your code will look like this which is the standard way to handle Redux level state changes in react.
class UserProfile extends Component {
constructor(props) {
super(props);
this.state = {
items: props.items
}
}
componentWillMount() {
console.log('------------ USER PROFILE -------------------');
}
componentWillRecieveProps({ items }) {
this.setState({ items });
}
componentDidMount() {
console.log('[ComponentDidMount]: Items: ', this.state.items);
this.props.fetchItems();
}
render() {
let profile = null;
console.log('[Render]: Items: ', this.state.items);
return <Auxillary>{profile}</Auxillary>;
}
}
const mapStateToProps = state => {
return {
items: state.items.items
};
};
const mapDispatchToProps = dispatch => {
return {
fetchItems: () => dispatch(actions.fetchItems())
};
};
export default connect(mapStateToProps, mapDispatchToProps)(UserProfile);
P.S Just making the API call inside componentWillMount will not help either as API call is async and can take up some time to resolve and till then react will finish rendering the component. so you'll still have to use componentWillRecieveProps
Standard practice is to call this.props.fetchItems() in your constructor or componentWillMount().
componentDidMount is called after render which is why your items do not render - they do not exist until after the initial render.
There are certain ways you can resolve this.
The very first time when render() gets called it was subscribed to the initial props/state that was initialise in redux store through redux connect method. In your case items was null.
Always initialise your redux store with some meaningful data.
In your case if items will be array you can initialise with empty array.
When you dispatch action your store will get updated and the component which was subscribed to items will be re rendered and in this way you donot have to use setState inside componentWillReceiveProps and you can avoid using it.
You need to handle certain cases in render like if array is empty and data is still loading then show some kind of loader and once data is fetched then display it.
Why is it good practice to assign state variables to React's state? I'm a react beginner. I'm trying to understand the purpose of a separate state object. Naively, it seems just as good (and a little shorter), to write in a component:
this.foo = 3;
instead of:
this.state.foo = 3;
I understand that setState triggers a rerender, but it seems you could just easily set the state you want and then trigger a rerender directly. I've been using this.setState({}).
In all other cases, React appears to adhere to principles of simplicity. React tends to favor native JavaScript functionality over additional React-defined functionality except where additional functionality is absolutely required. Why did the creators of React go out of their way to give React state?
In-Depth Example
As a longer example, I can define a component Counter that increases its count every second. The first way uses react's state property. The second attaches its state directly to the instance.
Using state:
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = {count: 0};
setInterval((() => {
this.setState(previousState => ({
count: previousState.count + 1 }));
}), 1000);
}
render() {
return <h1>{this.state.count}</h1>;
}
}
Attaching properties directly to the instance:
class Counter extends React.Component {
constructor(props) {
super(props);
this.count = 0;
setInterval((() => {
this.count += 1;
this.setState({})
}), 1000);
}
render() {
return <h1>{this.count}</h1>;
}
}
I'm trying to set the state of my PlayerKey component here however the state won't update on a onClick action:
class PlayerKey extends Component {
constructor(props) {
super(props);
this.state = {
activeKeys:[]
}
}
activateKey = (e) => {
this.setState({
activeKeys:["2","3"]
})
}
render() {
return (
<div className="key" data-row-number={this.props.rowKey} data-key-number={this.props.dataKeyNumber} onClick={this.activateKey}></div>
)
}
}
I've tried console logging this.state in activateKey and it gives me the state of the component no problem (the blank array) so not sure why I can't update it?
setState method in react is asynchronous and doesn't reflect the updated state value immediately.
React may batch multiple setState() calls into a single update for performance.
So accessing the recently set state value might return the older value. To see whether the state has really been set or not, You can actually pass a function as callback in setState and see the updated state value. React Docs
As in your case, you can pass a function as callback as follows.
activateKey = (e) => {
this.setState({
activeKeys:["2","3"]
}, () => {
console.log(this.state.activeKeys); // This is guaranteed to return the updated state.
});
}
See this fiddle: JSFiddle