how can i set a state variable as component's property in react native? - react-native

i started learning react native and building an android app.so i have facing some issue with setting and getting a component's property.here is my code
i have two components named as content-container and bar-chart.
inside content-container ,here is my code block:
state = {
barChartResponse: {},
arcChartResponse: {},
stackChartResponse: {},
lineChartResponse: {},
token:'abc',
};
componentWillMount() {
this.setState({token:'xyz'});
}
render() {
return (
<ScrollView>
<BarChart chartData = {this.state.token} />
</ScrollView>
);
}
now i am trying to get this property inside bar-chart component as follows:
constructor(props) {
super(props);
Alert.alert("ChartData is : ",props.chartData);
}
it displays me value what i set in state object by default i.e. abc, but i want updated value.
please help me to find out what am i doing wrong....... thanks in advance.

You can use componentWillRecieveProps but it is deprecated and in RN>54 you can use componentDidUpdate or getDerivedStateFromProps to get state from parent like this:
componentDidUpdate(nextProps){
if (this.props.chartData !== nextProps.chartData) {
alert(nextProps.chartData)
}
}
or
static getDerivedStateFromProps(props, current_state) {
if (current_state.chartData !== props.chartData) {
return {
chartData: props.chartData,
}
}
}

You need to update state of parent component it will automatically reflect in child component but next time you will receive in componentWillRecieveProps(nextProps) then render method.
for example:
state = {
barChartResponse: {},
arcChartResponse: {},
stackChartResponse: {},
lineChartResponse: {},
token:'abc',
};
componentWillMount() {
this.setState({token:'xyz'});
}
updateState = () => {
this.setState({token: "newToken"})
}
render() {
return (
<ScrollView>
<Button onPress={this.updateState}>update State</Button>
<BarChart chartData = {this.state.token} />
</ScrollView>
);
}
in BarChart.js
componentWillRecieveProps(nextProps) {
// you can compare props here
if(this.props.chartData !== nextProps.chartData) {
alert(nextProps.chartData)
}
}

Related

react-select (AsyncSelewhen typing) not deleting

I have a dropdown using AsyncSelect that when trying to clear it up it doesn't clear. As I have the setup right now I have to: display a previously selected value upon rendering (set by the state, which in turn gets its initial value from a prop), be able to update this value (onChange), be able to type into the input a string to search. Right now, after having a value pre-populated, when I click on the dropdown to type and search, my input doesn't get cleared and I don't see what I input, almost as if the input wasn't allowing edits.
Update: for example, when I click over the drop down I see the cursor but if I hit "delete" the value is not reset.
Any help will be appreciated it.
import React, { Component } from 'react';
import AsyncSelect from 'react-select/async';
class DropDown extends Component {
constructor(props) {
super(props);
this.state = {
selectedValue: null,
loaded: false,
};
this.onChange = this.onChange.bind(this);
this.loadOptions = this.loadOptions.bind(this);
}
componentDidMount() {
const { value } = this.props;
this.setState({ selectedValue: value, componentMounted: true });
}
onChange(value) {
const { updateParent } = this.props;
this.setState({ selectedValue: value });
updateParent(value);
}
// this returns a promise with my data
loadOptions() {
const { getDropDownOptions } = this.props;
return getDropDownOptions();
}
render() {
const { selectedValue, loaded } = this.state;
return (
loaded && (
<AsyncSelect
defaultOptions
isClearable
inputValue={selectedValue}
onChange={event => this.onChange(event)}
loadOptions={this.loadOptions}
{...{ ...this.props }}
/>
)
);
}
}
DropDown.defaultProps = {
isClearable: true,
};
export default DropDown;
You are passing the selected option which is of shape { value : '1', lable:'apple' } as the input value to the <AsyncSelect/>, there is no need to explicitly pass the input value into the component since it will be managed from inside Select component. Just change,
<AsyncSelect
...
inputValue={selectedValue}
...
/>
to
<AsyncSelect
...
value={selectedValue}
...
/>

Adding checked checkboxes to an array and removing the unchecked ones - react native

What I need to do is - add/remove the name of each checkbox(which are checked/unchecked by the user) in an array and send to the server. I am stuck in the following code. Any help is appreciated. Thankyou
class App extends Component<Props> {
render() {
return (
<View style={{ padding: 15 }}>
{
response.map(
item => {
return (
<CheckBoxItem label={item.name} />
);
}
)
}
</View>
);
}
}
CheckBoxItem.js
class CheckBoxItem extends Component<Props> {
state = {
check: false,
problemTypeArray: [],
}
changeArray = (label) => {
let array = [...this.state.problemTypeArray, label];
let index = array.indexOf(label);
console.log('array', array);//returns array with length 1 all the time
}
render() {
return (
<View>
<CheckBox value={this.state.check} onValueChange={(checkBoolean) => { this.setState({ check: checkBoolean }); this.changeArray(this.props.label); }} />
<MyText>{this.props.label}</MyText>
</View>
);
}
}
export default CheckBoxItem;
The real trick to this is to maintain a list of the selected items in the parent component. Each CheckBoxItem can control its own state but you will need to pass a value back to the parent component each time it is checked/unchecked.
As you haven't shown where your CheckBox component has come from, I will show you how to do it using the react-native-elements CheckBox. The principles can then be applied to your own CheckBox.
Here is the App.js
import CheckBoxItem from './CheckBoxItem'
export default class App extends React.Component {
// set some initial values in state
state = {
response: [
{
name:'first'
},
{
name:'second'
},
{
name:'third'
},
{
name:'fourth'
},
{
name:'fifth'
},
{
name:'sixth'
},
],
selectedBoxes: [] // this array will hold the names of the items that were selected
}
onUpdate = (name) => {
this.setState(previous => {
let selectedBoxes = previous.selectedBoxes;
let index = selectedBoxes.indexOf(name) // check to see if the name is already stored in the array
if (index === -1) {
selectedBoxes.push(name) // if it isn't stored add it to the array
} else {
selectedBoxes.splice(index, 1) // if it is stored then remove it from the array
}
return { selectedBoxes }; // save the new selectedBoxes value in state
}, () => console.log(this.state.selectedBoxes)); // check that it has been saved correctly by using the callback function of state
}
render() {
const { response } = this.state;
return (
<View style={styles.container}>
{
response.map(item => <CheckBoxItem label={item.name} onUpdate={this.onUpdate.bind(this,item.name)}/>)
}
</View>
);
}
}
Here is the CheckBoxItem
import { CheckBox } from 'react-native-elements'
class CheckBoxItem extends Component<Props> {
state = {
check: false, // by default lets start unchecked
}
onValueChange = () => {
// toggle the state of the checkbox
this.setState(previous => {
return { check: !previous.check }
}, () => this.props.onUpdate());
// once the state has been updated call the onUpdate function
// which will update the selectedBoxes array in the parent componetn
}
render() {
return (
<View>
<CheckBox
title={this.props.label}
checked={this.state.check}
onPress={this.onValueChange}
/>
</View>
);
}
}
export default CheckBoxItem;
Explanation
When a CheckBoxItem is created two things are passed to it. One is a label and the second is an onUpdate function. The onUpdate function links the CheckBoxItem back to the parent component so that it can manipulate the state in the parent.
The onUpdate function takes the previous value of the state, before this update is applied, and it looks to see if the name of the checkbox exists in the selectedBoxes array. If it exists in the selectedBoxes array it is removed, otherwise it is added.
Now there exists an array in the parent component that you can access that contains all that items that have been selected.
Snack
Want to try out my code? Well I have created a snack that shows it working https://snack.expo.io/#andypandy/checkboxes
Setting state
You may have noticed that I am doing some unusual things with setState. I am making sure that setState gets called properly by using the previous value of the state and then applying my updates to that. I am also using the fact that setState takes a callback to perform actions after the state has been updated. If you would like to read more here are some great articles on setState.
https://medium.learnreact.com/setstate-is-asynchronous-52ead919a3f0
https://medium.learnreact.com/setstate-takes-a-callback-1f71ad5d2296
https://medium.learnreact.com/setstate-takes-a-function-56eb940f84b6

React Native: Conditionally Render Components

I've searched this question and found a solution that said to conditionally render based on the state as follows:
render() {
const content = this.state.isReady ? <Home/> : <Splash/>;
return (
{content}
);
}
However, I keep getting an Invariant Violation: Objects are not valid a React child (found object with keys {content}.
your make typo, you returned Object, instead use between JSX elements:
const Ready = () => <div>Ready</div>
const NotReady = () => <div>NotReady</div>
class App extends Component {
constructor() {
super();
this.state = {
isReady: false
};
}
render() {
const content=this.state.isReady ? <Ready /> : <NotReady />
return (
<div>
{content}
</div>
);
}
}
Use simple if else instead of ternary expression because sometimes ternary operators "return" whatever's inside and can't execute blocks of code.
if (this.state.isReady) {
return <Home />
} else {
return <Splash />
}

Connected Component's prop doesn't update in React Native with Redux

I'm creating some kind of Realtime Chat App with React Native + Redux. When I get some new message from websocket, internally it will updates list of message as array with redux store. This is what looks like:
import { CHAT_INIT, CHAT_RECV } from '../actions/Chat';
const defaultState = {
chatList: []
};
export default function(state = defaultState, action = {}) {
switch(action.type) {
case CHAT_INIT:
return Object.assign({}, {
chatList: []
});
case CHAT_RECV:
let chatList = state.chatList;
chatList.push(action.data);
return Object.assign({}, {
chatList: chatList
});
default:
return state;
}
}
There are only two actions: CHAT_INIT and CHAT_RECV which can easily understand.
When app receives new message from socket, it will invoke store.dispatch with 'CHAT_RECV' action. This is the component code of list of messages:
class ChatList extends Component {
static propTypes = {
chatList: React.PropTypes.array
}
static defaultProps = {
chatList: []
}
componentWillMount() {
store.dispatch({
type: ChatActions.CHAT_INIT,
data: ''
});
}
componentWillReceiveProps(nextProps) {
console.log('will receive props'); // 1
}
render() {
console.log('<ChatList />::chatList', this.props.chatList); // 2
return (
<View style={styles.chatList}>
<Text>ChatList</Text>
</View>
);
}
}
export default connect(state => {
let chatList = state.ChatReducer.chatList;
console.log('Got:', chatList); // 3
return {
chatList: state.ChatReducer.chatList
};
})(ChatList);
I connected ChatList component with ChatReducer.chatList so when new message arrives, props of ChatList component will be update.
The problem is props on ChatList component doesn't updating at all! As you can see, I placed lots of console.log to tracking where is the problem. Numbers next of console.log is just added for easy explanation.
You can see that I'm trying to update chatList props of connected component ChatList, and it should be re-render on receive new props(means new message).
So [3] of console.log prints 'Got: [..., ...]' as well, but [1] and [2] are not prints anything! It means ChatList component didn't receive next props properly.
I double checked the code and tried to fix this, but not much works. Is this problem of Redux or React-Redux module? Previously I used both modules for my Electron ChatApp, and it worked without any problem.
Is there a something that I missed? I really don't know what is the matter . Anyone knows about this issue, please gimme a hand, and will be very appreciate it.
P.S. These are other component codes. I think it doesn't important, but I just paste it for someone who wants to know.
Superior component: App.js
export default class App extends Component {
componentDidMount() {
init(); // this invokes CHAT_INIT action.
}
render() {
return (
<Provider store={store}>
<ChatApp />
</Provider>
);
}
}
ChatApp.js which actually renders ChatList component:
class ChatApp extends Component {
render() {
return (
<View style={styles.container}>
<NavBar username={this.props.username} connected={this.props.connected} />
<ChatList connected={this.props.connected} />
<ChatForm connected={this.props.connected} />
</View>
);
}
}
export default connect(state => {
return {
username: state.UserReducer.username,
connected: state.NetworkReducer.connected
};
})(ChatApp);
You're mutating your state here:
case CHAT_RECV:
let chatList = state.chatList;
chatList.push(action.data);
return Object.assign({}, {
chatList: chatList
});
Instead, do:
case CHAT_RECV:
let chatList = state.chatList.concat(action.data);
return Object.assign({}, {
chatList: chatList
});

Show default element while loading Image

I have a component representing an user avatar that loads an image from my API.
I want it to display a default avatar (not another image) while the avatar is loading.
constructor() {
super();
this.state = {
loaded: false,
};
}
render() {
if (!this.props.uri || !this.state.loaded) {
return (
<DefaultAvatar />
);
}
return <Image onLoad={this.onLoad.bind(this)} uri={this.props.uri} />;
}
onLoad() {
this.setState({loaded: true});
}
The problem I have is that with this current code, the Image will never be rendered, so the state will never change. I'm unable to find a solution that would satisfy React principles and my requirements (no ghost components to load the image before displaying it).
class LazyImage extends React.Component{
constructor () {
super(this.props)
this.state = {loaded: false}
}
handleLoad () {
this.setState({loaded:true})
}
componentDidMount () {
this.img = new Image()
this.img.onload = this.handleLoad.bind(this)
this.img.src = this.props.src
}
render () {
return this.state.loaded?<img src={this.props.src}/>:<div>Loading...</div>
}
}
You create a native Image element and wait for it to load. Then you render the image with react. The browser is smart and fetches it from the cache this time. Instant render!
See http://jsfiddle.net/4hq3y4ra/3/ for a demo.
There are several ways this can be achieved, however to keep things simple, you can use a literal condition to toggle default avatar and the actual image.
constructor() {
super();
this.state = {
loaded: false,
};
}
onLoad(dataUri) {
if(dataUri !== undefined){
this.setState({loaded: true});
}
},
render() {
return (
<Image onLoad={this.onLoad} uri={this.state.loaded ? this.props.uri : 'default-avatar'} />
);
}
Image.prefetch will allow me to do what I want, thanks to everyone.