React Native ref is undefined when re-rendering a component - javascript

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>
)
}
}
}

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>
);
}
}

React native: passing image path as params through 2 components

i'm trying to pass an image path down to one component via react-navigation params and from that component to a third one as a prop.
This is a chat application and the structure of the components is:
ChatsMain.js --> (click on SingleChat, passing parameters when navigating) --> SingleConversation.js --> Message
I can pass 'name' as a parameter, but not the uri of an image, not even by assigning it to variables.
The name that i see on the SingleChat is passed without problems to the correspondent conversation, but when i pass the image i get the error:
'Failed prop type: Invalid prop source supplied to Image.'
I hope it's clear enough, i don't think it's the correct way to do this, but i am new to react native!
ChatsMain.js
const imageNames = {
juf: require('../assets/juf.png'),
meester: require('../assets/meester.png'),
groep: require('../assets/groep.png'),
onderwerpen: require('../assets/onderwerpen.png')
}
export default class ChatsMain extends React.Component {
render() {
return (
<SingleChat
image={imageNames.juf}
backgroundColor={yellow}
borderColor={blue}
name='Juf Elsa'
onPress={() => this.props.navigation.navigate('SingleConversation', {name: 'Juf Elsa', image: imageNames.juf})}
/>
)
SingleConversation.js
export default class SingleConversation extends React.Component {
static navigationOptions = ({ navigation }) => {
return {
title: navigation.getParam('name', 'Chat'),
headerStyle: [titleBar, { backgroundColor: yellow }],
headerTitleStyle: [title, { color: blue }]
}
}
render() {
return (
<View style={chatMainContainer}>
<ScrollView style={chatsContent}>
<Message image={({navigation}) => navigation.getParam('image', 'image')} />
</ScrollView>
</View>
);
}
}
Message.js
export default class ReceivedFirst extends React.Component {
render(
){
return(
<View style={textWithPicture}>
<View style={[chatPhotoContainer, { borderColor: yellow }]}>
<Image source={this.props.image} style={{ width: 70, height: 70}} />
.....
.....
)
You can use this.props.navigation.state.params instead this.props.navigation.getParam
export default class SingleConversation extends React.Component {
constructor(props) {
super(props);
this.state = {
image: this.props.navigation.state.params.image
};
}
...
Message image={this.state.image} />
OR
You need to get a Navigation from the props.
render() {
const { navigation } = this.props
return (
<View style={chatMainContainer}>
<ScrollView style={chatsContent}>
<Message image={() => navigation.getParam('image', 'image')} />
</ScrollView>
</View>
);
}

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 Pass array from child to parent component

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?

React Native - How to navigate to main body after IntroSliders

I'm trying to integrate opening app intro sliders to my app, but failing to connect the points to get from the intro to my main app body.
The library i'm using says use 'react-native-app-intro-slider' as such, where an _onDone() function is called to finish the intro and show the 'real' app:
export default class App extends React.Component {
_onDone = () => {
// User finished the introduction. Show "real" app
}
render() {
return (
<AppIntroSlider
slides={slides}
onDone={this._onDone}
/>
);
}
}
With the main body of my app (it works when I run it without the intro-slider addition) being this:
render() {
const contents = collection.map((item, index) => {
return (
<Card key={index}>
[[there's stuff here omitted]]
</Card>
)
});
return (
<View style={{flex:1}}>
<CardStack>
{contents}
</CardStack>
</View>
);
How can I get this to render after the introslider? Do I put all that inside the _onDone() function? (this doesn't work). Or is there a way write _onDone so that after the component, the regular part of the main app would proceed to go as before?
export default class App extends React.Component {
_onDone = () => {
// Something that lets me pass onto the main part of my app
}
render() {
return (
<AppIntroSlider
slides={slides}
onDone={this._onDone}
/>
// The main body of the app that I want React to get to after the <AppIntroSlider> component.
const contents = collection.map((item, index) => {
return (
<Card key={index}>
[[there's stuff here omitted]]
</Card>
)
});
return (
<View style={{flex:1}}>
<CardStack>
{contents}
</CardStack>
</View>
);
);
}
}
If you're not using a navigation library, I would suggest to simply use state:
constructor(props) {
super(props);
this.state = {
showRealApp: false
}
}
_onDone = () => {
this.setState({ showRealApp: true });
}
render() {
if (this.state.showRealApp) {
const contents = collection.map((item, index) => (
<Card key={index}>
{...}
</Card>
));
return (
<View style={{flex:1}}>
<CardStack>
{contents}
</CardStack>
</View>
);
} else {
return <AppIntroSlider slides={slides} onDone={this._onDone}/>;
}
}
You can also consult issue #18 on the react-native-app-intro-slider repo.

Categories