React Native Pass array from child to parent component - javascript

I have two components and i am trying to pass array state on back button to parent component but its not showing any data.
Screen1.js
class Screen1 extends Component {
constructor(props) {
super(props);
this.state = {
selectedRecord: []
};
}
updateData = (data) => {
this.setState({selectedRecord: data})
}
render() {
const {container} = styles;
return (
<View style={container}>
<Button onPress={() => alert(this.state.selectedRecord)}>
Show Value
</Button>
</View>
);
}
}
I created a method on Screen2.js
class Screen2 extends Component {
constructor(props) {
super(props);
this.state = {
record: [1, 2, 3, 4, 5]
};
}
backStock() {
this.props.navigation.state.params.updateData(this.state.record);
this.props.navigation.goBack()
}
render() {
const {container} = styles;
return (
<View style={container}>
<Button onPress={() => this.backStock()} transparent>
<Icon name='arrow-back'/>
</Button>
</View>
);
}
}
And backStock() method runs when i click on my back button. But when i tried to print value in console it was empthy. Am i missing something, is there a smooth way to pass states onback method?

Related

Call function with setState from other component

I have the following setup:
import {getNewImage} from '...'
export default class FirstClass extends Component {
constructor() {
super();
this.state = {
imageURL: 'www.test.com/new.jpg',
}
}
update = () => {
this.setState({
imageURL: 'www.test.com/updated.jpg',
})
}
render() {
return (
<View>
<Image
source={{ uri: this.state.imageURL }}
/>
</View>
);
}
}
import Class1 from '...'
export default class SecondClass extends Component {
render() {
return (
<TouchableOpacity onPress={() => new FirstClass().update()}>
<Class1></Class1>
</TouchableOpacity>
);
}
}
The problem is: it doesn't update the imageURL. I tried multiple things. Logging this inside of update gave back the right object. But trying to log the this.setState() gives an undefinded back.
I suppose by Class1 you mean FirstClass.
you should use the reference of the component using ref, not creating new instance of FirstClass class
checkout this code
export default class SecondClass extends Component {
private firstClass = null;
render() {
return (
<TouchableOpacity onPress={() => this.firstClass.update()}>
<Class1 ref={ref => this.firstClass = ref} />
</TouchableOpacity>
);
}
}
You can try to update state in Class1 similar to what do you want by the react reference.
But on my opinion this is non a good case and predictable behavior to change child component state from parent.
As another options you can add additional state to the SecondClass and pass it via props to child component. And inside Class1 in getDerivedStateFromProps based on that prop change state.
export default class FirstClass extends Component {
constructor() {
super();
this.state = {
isNeedToUdpate: false,
imageURL: "www.test.com/new.jpg"
};
}
static getDerivedStateFromProps(props, state) {
if (props.isNeedToUdpate !== state.isNeedToUdpate) {
return {
isNeedToUdpate: props.isNeedToUdpate,
imageURL: "www.test.com/updated.jpg"
};
}
return null;
}
render() {
return (
<View>
<Image source={{ uri: this.state.imageURL }} />
</View>
);
}
}
export default class SecondClass extends Component {
constructor() {
super();
this.state = {
isNeedToUpdateClass1: false
};
}
render() {
return (
<TouchableOpacity
onPress={() => {
this.setState({ isNeedToUpdateClass1: true });
}}
>
<Class1 isNeedToUpdate={this.state.isNeedToUpdateClass1}></Class1>
</TouchableOpacity>
);
}
}

How to share or to recive some data from second screen In React Native

I have 2 screens, my Home Screen
class Home extends Component {
constructor(props) {
super(props)
this.state = {
myDebts: 745.8455656,
debts: 1745.54555
}
}
addFriendsHandler = () => {
Alert.alert('You tapped the button!')
}
render () {
return (
<View style={{flex: 1}}>
<Header
text={"Splitwise"} />
<Debts
myDebts={this.state.myDebts}
debts={this.state.debts}/>
<Buttons text={"+ ADD FRIENDS ON SPLITWISE"}
clicked={() => this.props.navigation.navigate("AddFriend")}/>
</View>
)
}
}
export default Home
and my second Screen
class AddFriendPage extends Component{
state = {
name: ''
}
addFriendHandler = () => {
this.props.navigation.navigate("MainPage")
}
render() {
return (
<View>
<Header text={"Add a friend"}/>
<Sae
label={'Your friends name'}
labelStyle={{ color: '#47AE4f' }}
iconClass={FontAwesomeIcon}
iconName={'pencil'}
iconColor={"#47AE4f"}
inputStyle={{ color: '#000' }}
onBlur={(e) => this.setState({name: e.nativeEvent.text})}
/>
<Buttons text={"+ ADD FRIEND"}
disable={this.state.name === ''}
clicked={this.addFriendHandler}/>
</View>
)
}
}
and my Navigator
export default class App extends React.Component {
render() {
return (
<AppStackNavigator />
);
}
}
const AppStackNavigator = createStackNavigator({
MainPage: Home,
AddFriend: AddFriendScreen
})
I want to send a function to the AddFriendPage screen from Home screen, and inside that function i want to get value from input and return the name back into Home screen, but unfortunately i have no idea how to share data between 2 screens
https://reactnavigation.org/docs/en/params.html#docsNav
You want to pass params during navigation:
() => this.props.navigation.navigate("AddFriend", {name: "Alan"})
Then in the parent method (if you want to display it, you could just put it in render):
const name = this.props.navigation.getParam(name, null)
If null, you know that the screen was reached from a different screen, and can handle that case normally. You can add whatever params you want.

React Native property not passed to child component

I have the following problem.
I am creating a React Native appliation and I want to pass a click handler to a child component. But when I try to call the click handler in the child component I keep getting a _this.props.onItemPress is not a function Exception.
When I try to pass the function with a .bind(this) inside the parent, it says the function is undefined.
Here's my code:
Parent
constructor(props) {
super(props)
this.handleTodoPress = this.handleTodoPress.bind(this)
}
...
handleTodoPress(event) {
console.warn('Press handled')
}
renderItem ({section, item}) {
return <TodoItem onItemPress={this.handleTodoPress} title={item.title} description={item.description} completed={item.completed} />
}
...
render () {
return (
<View style={styles.container}>
<SectionList
renderSectionHeader={this.renderSectionHeader}
sections={this.state.data}
contentContainerStyle={styles.listContent}
data={this.state.dataObjects}
renderItem={this.renderItem}
keyExtractor={this.keyExtractor}
initialNumToRender={this.oneScreensWorth}
ListHeaderComponent={this.renderHeader}
SectionSeparatorComponent={this.renderSectionSeparator}
ListEmptyComponent={this.renderEmpty}
ItemSeparatorComponent={this.renderSeparator}
renderSectionFooter={this.renderSectionFooter}
/>
</View>
)
}
}
Child
import React, { Component } from 'react';
import { TouchableOpacity, View, Text, } from 'react-native';
import styles from './Styles/TodoItemStyles'
export default class TodoItem extends Component {
constructor(props) {
super(props)
this.state = {completed: 'Todo'}
this.setCompletedState = this.setCompletedState.bind(this)
}
itemPressed = (e) => {
console.warn(this.props);
this.props.onItemPress(e)
}
setCompletedState() {
if (this.props.completed == true) {
this.setState({completed: 'Completed'})
}
}
componentWillMount() {
this.setCompletedState()
}
render() {
return (
<TouchableOpacity onPress={this.itemPressed}>
<View style={styles.todoContainer}>
<Text style={styles.itemTitle}>{this.props.title}</Text>
<Text style={styles.itemDescription}>{this.props.description}</Text>
<Text style={[styles.itemLabel, this.props.completed ? styles.itemLabelCompleted : styles.itemLabelNotCompleted]}>{this.state.completed}</Text>
</View>
</TouchableOpacity>
);
}
}
TRY:
export default class TodoItem extends Component {
constructor(props) {
super(props)
this.state = {completed: 'Todo'}
this.setCompletedState = this.setCompletedState.bind(this)
}
itemPressed(e){
console.warn(this.props);
this.props.onItemPress(e)
}
setCompletedState() {
if (this.props.completed == true) {
this.setState({completed: 'Completed'})
}
}
componentWillMount() {
this.setCompletedState()
}
render() {
return (
<TouchableOpacity onPress={this.itemPressed}>
<View style={styles.todoContainer}>
<Text style={styles.itemTitle}>{this.props.title}</Text>
<Text style={styles.itemDescription}>{this.props.description}</Text>
<Text style={[styles.itemLabel, this.props.completed ? styles.itemLabelCompleted : styles.itemLabelNotCompleted]}>{this.state.completed}</Text>
</View>
</TouchableOpacity>
);
}
}
when you use
itemPressed = (e) => {
console.warn(this.props);
this.props.onItemPress(e)
}
that notations binds the current context inside the function
I think your problem is that how you are using arrow function for itemPressed. Try rewriting it and binding this for itemPressed the same as you did for setCompletedState.

React Native ref is undefined when re-rendering a component

I have a problem with refs on React Native. This is a simplified version of my code:
class Main extends React.Component {
constructor(props) {
...
this.refs = {};
}
render() {
if(this.state.page=="index") {
return(
<View>
<FlatList ref={flatlist => this.refs.flatlist = flatlist}> ... </FlatList>
<MyActionButton flatlist={this.refs.flatlist}/>
</View>
)
} else if (this.state.page="text"=){
return(
<Text> ... </Text>
)
}
}
}
class MyActionButton extends React.Component {
render(
return(
<ActionButton>
<ActionButtonItem onPress={() => {
console.log("AB props", this.props)
}} />
</ActionButton>
)
)
}
The app starts with this.state.page = "index" so when I press MyActionButton I see the log as expected, and things seem to work:
'AB props', {flatlist: {A LOT OF STUFF HERE}}
However If I change the state.page to "text" and then come back to "index" again, when I press MyActionButton I get:
'AB props', {flatlist: undefined}
I'm not sure why that prop gets undefined and how to fix it to make it point to the actual FlatList.
I don't like very much, but I managed to get it working by changing the reference to a getter funcion
class Main extends React.Component {
constructor(props) {
...
this.refs = {};
}
getFlatList() {
return this.refs.flatlist;
}
render() {
if(this.state.page=="index") {
return(
<View>
<FlatList ref={flatlist => this.refs.flatlist = flatlist}> ... </FlatList>
<MyActionButton flatlist={this.getFlatList.bind(this)}/>
</View>
)
} else if (this.state.page="text"=){
return(
<Text> ... </Text>
)
}
}
}

Updating Parent Component via Child Component in React Native

I have a parent component and two child components. One of the child components is a form with an add button. I am able to capture the text when add button is pressed, but stuck when trying to pass that value to the parent component, update the array in parent component and re-render the view.
Parent Component:
var Tasks = ['Competitor Study','Content Plan','Write','Promote','Consumer Research']
//Build React Component
class MyTaskList extends Component {
render() {
return (
<View style={styles.container}>
<Text style={styles.titleStyle}>
My Tasks
{"\n"}
({Moment().format("MMM Do YY")})
{"\n"}
</Text>
<AddTask />
{this.workitems()}
</View>
);
}
workitems() {
return (Tasks.map(function(workitem,i) {
return <ListItem key={i} workitem={workitem} />
}
)
);
}
}
Child component with the form
class AddTask extends Component {
constructor(props) {
super(props);
this.state = {
enterTask: 'Enter task'
};
}
onTaskTextChanged(event) {
this.setState({ enterTask: event.nativeEvent.text });
}
onAddPress() {
var newtask = this.state.enterTask;
console.log('new task - '+newtask);
}
render() {
return (
<View style={styles.addTask}>
<TextInput
style={styles.taskInput}
value={this.state.enterTask}
onChange={this.onTaskTextChanged.bind(this)}
placeholder='Enter task'/>
<TouchableHighlight style={styles.button}
onPress={this.onAddPress.bind(this)}
underlayColor='#99d9f4'>
<Text style={styles.buttonText}>Add</Text>
</TouchableHighlight>
</View>
);
}
}
I would instead use state for this as using forceUpdate() is not recommended
From React docs:
Calling forceUpdate() will cause render() to be called on the component, skipping shouldComponentUpdate(). This will trigger the normal lifecycle methods for child components, including the shouldComponentUpdate() method of each child. React will still only update the DOM if the markup changes.
Normally you should try to avoid all uses of forceUpdate() and only read from this.props and this.state in render(). This makes your component "pure" and your application much simpler and more efficient.
(Based off of #1ven answer)
Parent Component:
//Build React Component
class MyTaskList extends Component {
constructor(props) {
super(props);
this.state = {
tasks: [
'Competitor Study',
'Content Plan',
'Write','Promote',
'Consumer Research'
]
}
}
handleAddTask(task) {
var newTasks = Object.assign([], this.state.tasks);
newTasks.push(task);
this.setState({tasks: newTasks});
}
render() {
return (
<View style={styles.container}>
<Text style={styles.titleStyle}>
My Tasks
{"\n"}
({Moment().format("MMM Do YY")})
{"\n"}
</Text>
<AddTask onAddTask={this.handleAddTask.bind(this)} />
{this.workitems()}
</View>
);
}
workitems() {
return this.state.tasks.map(function(workitem,i) {
return <ListItem key={i} workitem={workitem} />
});
}
}
Child component with the form
class AddTask extends Component {
constructor(props) {
super(props);
this.state = {
enterTask: 'Enter task'
};
}
onTaskTextChanged(event) {
this.setState({ enterTask: event.nativeEvent.text });
}
onAddPress() {
var newtask = this.state.enterTask;
console.log('new task - '+newtask);
// Providing `newtask` variable to callback.
this.props.onAddTask(newtask);
}
render() {
return (
<View style={styles.addTask}>
<TextInput
style={styles.taskInput}
value={this.state.enterTask}
onChange={this.onTaskTextChanged.bind(this)}
placeholder='Enter task'/>
<TouchableHighlight style={styles.button}
onPress={this.onAddPress.bind(this)}
underlayColor='#99d9f4'>
<Text style={styles.buttonText}>Add</Text>
</TouchableHighlight>
</View>
);
}
}
You should provide handleAddTask callback from parent to child component:
var Tasks = ['Competitor Study','Content Plan','Write','Promote','Consumer Research']
//Build React Component
class MyTaskList extends Component {
handleAddTask(task) {
// When task will be added, push it to array
Tasks.push(task);
}
render() {
return (
<View style={styles.container}>
<Text style={styles.titleStyle}>
My Tasks
{"\n"}
({Moment().format("MMM Do YY")})
{"\n"}
</Text>
<AddTask onAddTask={this.handleAddTask} />
{this.workitems()}
</View>
);
}
workitems() {
return (
Tasks.map(function(workitem,i) {
return <ListItem key={i} workitem={workitem} />
})
);
}
}
Next, you should pass task from child component to this callback:
class AddTask extends Component {
constructor(props) {
super(props);
this.state = {
enterTask: 'Enter task'
};
}
onTaskTextChanged(event) {
this.setState({ enterTask: event.nativeEvent.text });
}
onAddPress() {
var newtask = this.state.enterTask;
console.log('new task - '+newtask);
// Providing `newtask` variable to callback.
this.props.onAddTask(newtask);
}
render() {
return (
<View style={styles.addTask}>
<TextInput
style={styles.taskInput}
value={this.state.enterTask}
onChange={this.onTaskTextChanged.bind(this)}
placeholder='Enter task'/>
<TouchableHighlight style={styles.button}
onPress={this.onAddPress.bind(this)}
underlayColor='#99d9f4'>
<Text style={styles.buttonText}>Add</Text>
</TouchableHighlight>
</View>
);
}
}
That's it. Hope, it helps!

Categories