How to add value to already existing state in react native? - react-native

I have a state property called projectname
this.state={
projectname: ""
}
I need to change the state value (for example from "Project" to "Project-01" ) when a button is clicked.
Here state projectname changes dynamically. I want to know to how to add -01 with project name

You need to use this.setState() to change your component's state.
The common way to do this by using the onPress handler of the Button component:
create a class method that uses this.setState
bind such method in the class constructor
pass the method to the onPress prop of a Button
Here is an example
import React from "react";
import { View, Button } from "react-native";
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
projectname: "Project", // Inital value in your example
};
// Binding the method that will handle the click.
this.onClickHandler = this.onClickHandler.bind(this);
}
// The actual method that will alter the state.
onClickHandler() {
this.setState({
projectname: "Project-01",
});
}
render() {
return() (
<View>
{/* A button with the method passed to onPress. */}
<Button title="Click me" onPress={this.onClickHandler} />
</View>
);
}
}
Now, if you explicitly want to add "-01" to the projectname value, you need to reference the current value of projectname when altering the state:
onClickHandler() {
this.setState({
projectname: this.state.projectname + "-01"
});
}
But you have to be careful because multiple clicks will add multiple times "-01" (for example, 4 clicks will result in "Project-01-01-01-01").

I'm not sure I got your point correctly or not but I think you are looking for something like this
constructor(props) {
super(props);
this.state = {
projectname:"projectname"
};
this.updateState= this.aba.bind(this)
}
updateState () {
this.setState({
projectname : this.state.projectname.concat("-01")
})
}

You can use setState method to change the state like this :
this.setState({
projectname : "new Project name"
})

Related

changing state error on component will unmount

I have a class component which user may use to c change language of the App , I have added this screen to create Stack navigator , my issue is when I navigate to it , the console displays Error saying :
Warning: Can't perform a React state update on an unmounted component.
This is a no-op, but it indicates a memory leak in your application.
To fix, cancel all subscriptions and asynchronous tasks in the
componentWillUnmount method.
I am not seeing that I am calling component will unmount , could you please help here is my code :
class Languages extends Component {
constructor(props) {
super(props);
this.state = {
loc : null
};
}
onChangeLan(lanz) {
this.setState({ loc : lanz})
this.props.languaje(lanz.locale)
}
render() {
return (
<View style = { styles.contianer}>
{
Langua.map( (lan) =>{
return (
<ListItem
key = {lan.id}
title= {lan.name}
onPress = { this.onChangeLan.bind(this,lan)}
/>
)
})
}
<Text>{I18n.t('login')}</Text>
</View>
);
}
}
const mapStateToProps = state =>{
return {
language : state.lan.locale
}
}
export default connect(mapStateToProps,{languaje})(Languages);
const styles = StyleSheet.create({
contianer : {
flex: 1
}
})
That error indicates that you're attempting to use setState on a component that has unmounted. The error is trying to help you by indicating that any asynchronous action that could potentially result in a setState should be cancelled when the component unmounts, not that you have an error in your componentWillUnmount method. Take a look at the stack trace and find where the error is originating from.

How to make every React Component reload based on selected value of picker?

How can I force reload certain or all react Components when a particular value is selected using Picker?
import React from 'react';
import { Picker, Text, View } from 'react-native';
export default class Test extends React.Component {
render(){
return (
<View>
<TestComponent />
<Text>
{defaultKey + ' from defaultKey from main class'}
</Text>
</View>
);
}
}
let myArr = [
{
"key": "key0",
"value": "value0"
},
{
"key": "key1",
"value": "value1"
}
];
let defaultKey = "key0";
class TestComponent extends React.Component {
constructor (props){
super(props);
this.state = {
selectedValue : "value0",
PickerValueHolder : "value0"
}
}
render(){
return (
<View>
<Picker
selectedValue ={ this.state.PickerValueHolder }
onValueChange ={ (itemValue, itemIndex) => {
this.setState({ PickerValueHolder: itemValue });
defaultKey = myArr[itemIndex].key;
defaultIndex = itemIndex;
}
}
>
{ myArr.map((item, key)=>(
<Picker.Item label={ item.key } value={ item.value } key={ key } />)
)}
</Picker>
<Text>
{defaultKey + ' from defaultKey from TestComponent'}
</Text>
</View>
);
}
}
In this case, the <Text> in the default class is not reloading. How do I make it reload? And how can I force reload Imported components as well?
React Components renders / refresh whenever state / props changes. If you want to re render current component change it's state. If you want to render the child component change it's props. If you want to re render the parent component use a callback and modify the state in callback.
You could use global state management like redux, but if you're unfamiliar with that, you can send a handler into the Test Component as a callback to the Parent via the props.
Changing current component value use
this.state
Changing a child component value use and pass the prop in by
Inserting Props to child:
<ChildComponent prop1={value}/>
Getting the value from the child component:
this.props.prop1
Updating the Parent state is similar to the Above but requires the prop to be a function passed from the parent.
handler () {// some function in parent component}
<ChildComponent prop1={this.handler}/>
It would mean it would look something like below:
import React from 'react';
import { Picker, Text, View } from 'react-native';
export default class Test extends React.Component {
constructor(props) {
super(props)
this.handler = this.handler.bind(this);
this.state = {
defaultKey : "somevalue"
};
}
handler(value) {
this.setState({
defaultKey: value
})
}
render(){
return (
<View>
<TestComponent handler={this.handler}/>
<Text>
{this.state.defaultKey + ' from defaultKey from main class'}
</Text>
</View>
);
}
}
let myArr = [
{
"key": "key0",
"value": "value0"
},
{
"key": "key1",
"value": "value1"
}
];
let defaultKey = "key0";
class TestComponent extends React.Component {
constructor (props){
super(props);
this.state = {
selectedValue : "value0",
PickerValueHolder : "value0"
}
}
render(){
return (
<View>
<Picker
selectedValue ={ this.state.PickerValueHolder }
onValueChange ={ (itemValue, itemIndex) => {
this.setState({ PickerValueHolder: itemValue });
defaultKey = myArr[itemIndex].key;
defaultIndex = itemIndex;
this.props.handler(defaultKey);
}
}
>
{ myArr.map((item, key)=>(
<Picker.Item label={ item.key } value={ item.value } key={ key } />)
)}
</Picker>
<Text>
{defaultKey + ' from defaultKey from TestComponent'}
</Text>
</View>
);
}
}
UPDATE:
The OP has asked about why to use redux (as some of the components are not nested through the Test class.
What is redux?
Redux is a predictable state container for JavaScript apps.It helps you write applications that behave consistently.
Check out the docs here: https://redux.js.org/
Essentially, you've entered an issue a lot of people face when building an application that needs to share state between components in React/React Native. In every component you have a local state (this.state) - Redux includes a global App State, therefore in your classes where you change your default value, you can update the global store (which all components have access to). The value which you display the defaultValue would be from the global store.
Essentially, components which are not related to each other, will not know about each others state you'll need to use a global store such as redux. I suggest you do some research on the best technique for you. One way you could achieve it at present would be to wrap the whole application in a parent component and pass the props down from there, such as
-- ParentComponent (set state here)
-- ChildComponent1 (Pass Props)
-- ChildComponentOf1 (Pass Props)
-- ChildComponent2 (Pass Props)
For some further reading check out some posts here:
How to update parent's state in React?
How to share state among multiple scenes of a Navigator in React-Native

react-native window.setInterval, trying to display an incrimenting number

I want to display a number that increases once every second, rather than using some kind of time function I am using window.setInterval because eventually I will replace this with some more complicated code that instead of just displaying the number calls some function and displays the result. Here is the code I have right now:
export default class Test extends Component {
constructor(props){
super(props)
this.state = {counter:0}
}
render(){
newCounter = this.state.counter+1 // I am going to replace this
// with something like:
// newCounter = someFunc()
func = this.setState
window.setInterval(function(){
func({counter:newCounter})
},1000);
return(
<View>
<Text>{this.state.counter}</Text>
</View>
)
}
}
It gives me this error: undefined is not an object (evaluating 'this.updater')
how can I do this correctly? Thanks
Try this:
export default class Test extends Component {
constructor(props){
super(props)
this.state = {counter:0}
}
componentDidMount(){
setInterval(() => {
this.setState( prevState => ({counter: prevState.counter + 1}));
},1000);
}
render(){
return(
<View>
<Text>{this.state.counter}</Text>
</View>
)
}
}
Basically, you want to use the function arrow with set Interval, to not lose the context of the this keyword, and try to use set State outside of the render method.
Here is a demo as well: https://snack.expo.io/HkikISZMm

How do I alternate two Images in a react native component

I'm trying to animate an icon in React native by simply switching the image every 500ms. My code looks like this:
export default class FlashingIcon extends Component {
constructor(props) {
super(props);
this.state = {
on: true,
};
setInterval(() => {
this.setState(previousState => {
return {
on: !previousState.on,
};
});
}, 500);
}
render() {
let sprite = this.state.on
? require('../onIcon.png')
: require('../offIcon.png');
return (
<Image
source={sprite}
style={{width:16, height:20}}
/>
);
}
}
The code is basically copy-and-pasted from:
https://facebook.github.io/react-native/docs/state.html and
https://facebook.github.io/react-native/docs/images.html
Each image shows up if I just copy the require into the <Image>. I can also verify that if I instead render a <Text> element outputting this.state.on, it shows the correct value alternating.
I can't for the life of me work out what I've done wrong.
Add key to Image It will help in re-rendering image once state changed.
<Image
key={this.state.on}
source={sprite}
style={{width:16, height:20}}
/>

Refresh overview scene after changing state in another scene with react / redux / react-native-router-flex

Most simplified working example provided in github !!!
I have a simple app to learn building apps with react native and redux. From my understanding if you display data from the redux state in your render method and then values of this state is changed, then the value will be changed as well and react rerenders all components which needs to be rerendered due to the state change.
I have the application available on github: https://github.com/schingeldi/checklist
Its really simple. I have an overview, if you click on the status of an entry, you get to a detailed page. If you click on "Mark xxx" the status in changed in the redux state (according to logs) but its not refreshed in the overview scene.
Basically I have an Overview.js:
class Overview extends Component {
constructor(props) {
super(props);
this.state = {fetching:false};
}
entries() {
// console.log("Overview");
// console.log(this.props);
// console.log(this.props.entries);
return Object.keys(this.props.entries).map(key => this.props.entries[key]);
}
componentDidMount() {
this.setState({fetching:true});
this.props.actions.getEntries()
.then( (res) => {
this.setState({fetching: false});
})
}
handleChange(entryId) {
Actions.detail({id: entryId});
}
render() {
return (
<View>
<ScrollView>
{ !this.state.fetching && this.entries().map((entry) => {
return (
<TouchableHighlight key={entry.id}>
<View >
<Text>{entry.name}</Text>
<TouchableHighlight onPress={(entryId ) => this.handleChange(entry.id)}><Text>{entry.status}</Text></TouchableHighlight>
<Text>---------------------------</Text>
</View>
</TouchableHighlight>
)
}
)
}
{this.state.fetching ? <Text>Searching </Text> : null }
</ScrollView>
</View>
)}
}
function mapStateToProps(state) {
return {entries: state.default.entries };
}
function mapDispatchToProps(dispatch) {
return {actions: bindActionCreators(actions,dispatch)};
}
export default connect(mapStateToProps, mapDispatchToProps)(Overview);
When clicking on the Status ( {entry.status} ) I open another Scene Details.js:
class Detail extends Component {
constructor(props) {
super(props);
this.state = {}
}
componentWillMount() {
this.setState({
entry: this.props.entries[this.props.id]
})
}
patchEntry(newStatus) {
console.log("Details: patchEntry with " + this.props.id +" and " + newStatus );
this.props.actions.patchEntry(this.props.id, newStatus);
}
render() {
return (
<View>
<Text>{this.state.entry.name}</Text>
<TouchableHighlight onPress={() => this.patchEntry('done')}><Text>Mark done</Text></TouchableHighlight>
<TouchableHighlight onPress={() => this.patchEntry('cancelled')}><Text>Mark cancelled</Text></TouchableHighlight>
</View>
)
}
}
function mapStateToProps(state) {
console.log(state);
return {entries: state.default.entries };
}
function mapDispatchToProps(dispatch) {
return {actions: bindActionCreators(actions,dispatch)};
}
export default connect( mapStateToProps, mapDispatchToProps)(Detail);
And I have an action and a reducer which are called perfectly fine when one of the TouchableHighlights are pressed. I even see in the logs that the state is changed when outputting the whole state.
But my question is, how do I get the status refreshed on the Overview scene, once I got back (pop) from the Detail scene?
If you need anymore information let me know, but it should be simple to reproduce as I wrote a whole working app. Just clone, npm install and run it.
Thanks a lot for your help.
I did a quick look into your code and here are some suggestions/information.
In you Detail.js file you're setting your state once the component is mounted.
When you update your redux store and get the refreshed props, it won't update your UI because it's reflecting your state, and your state won't get the new value because you're only setting it on componentWillMount method. Check more information here in the docs.
Also it seems it's not very clear for you when to use the React component's state.
In this example, from Detail.js file you don't need the component's state at all. You can compute that value directly from the properties.
Ex:
render() {
const entry = this.props.entries[this.props.id];
return (
<View>
<Text>{entry.name}</Text>
<TouchableHighlight onPress={() => this.patchEntry('done')}><Text>Mark done</Text></TouchableHighlight>
<TouchableHighlight onPress={() => this.patchEntry('cancelled')}><Text>Mark cancelled</Text></TouchableHighlight>
</View>
)
}
You could even do that inside your mapStateToProps function. More info here.
Ex:
function mapStateToProps(state, ownProps) {
return {
entries: state.default.entries,
entry: state.default.entries[ownProps.id],
};
}
It seems your Overview.js file is OK regarding the UI being updated, because it's render method is reflecting the props and not it's state.
UPDATE 06/27
I've just checked your reducers and you may have some fixes to do there as well.
case ENTRY_PATCHING:
let patchedEntries = state.entries;
patchedEntries[action.data.entryId].status = action.data.newStatus;
return {...state,
entries: patchedEntries
}
In this reducer you're mutation your state, and you must not do that. The redux store can't be mutated. You can check more details about it here http://redux.js.org/docs/recipes/reducers/ImmutableUpdatePatterns.html
So, fix example:
case ENTRY_PATCHING:
const patchedEntry = {
...state.entries[action.data.entryId],
status: action.data.newStatus
}
return {
...state,
entries: {
...state.entries,
[action.data.entryId]: patchedEntry,
}
}