I have a parent component App.js which looks like this
export default class App extends React.Component {
constructor(){
super();
this.state = {
registerusername: '',
}
}
Now I have a child component ChildComp.js which is a direct child of App.js and looks like this
class ChildComp extends React.Component {
render() {
return (
<View>
<TextInput
placeholder="Username"
placeholderTextColor = 'black'
onChangeText={(registerusername) => this.setState({registerusername})}
/>
<Text>
{this.state.registerusername.split(' ').map((word) => word && '🍕').join(' ')}
</Text>
</View>
}
}
I am attempting to change print the pizza logo just like the react native document under the TextInput field. I am having a hard time understanding how I can do this when the state, registerusername is in parent component and the ChildComp is stateless. I am doing `this.setState({registerusername})} in my ChildComp but I dont think it is referencing the parent's state.
in app'
setName = (name) => {
this.setState({ registerusername: name })
}
render() {
return (
<div>
<ChildComp registerusername={this.state.registerusername} setName={this.setName} />
</div>
)
}
in ChildComp
< TextInput
placeholder = "Username"
placeholderTextColor = 'black'
onChangeText = {(e) => this.props.setName(e.target.value)}
/>
< Text >
{ this.props.registerusername.split(' ').map((word) => word && '🍕').join(' ') }
</Text >
Related
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>
);
}
}
in Parent component, I'm trying to focus the TextInput of second Child component when submit button is pressed in the TextInput of the first Child component but this error comes up: error message
Child.js
import React from "react";
import { View, TextInput} from "react-native";
export default class Child extends React.Component {
focus = ref => {
ref.input.focus();
};
render() {
return (
<View>
<TextInput
placeholder='enter text'
onSubmitEditing={() => {
this.focus(this.props.destinationRef);
}}
ref={input => {
this.input = input;
}}
/>
</View>
);
}
}
Parent.js
import React from "react";
import Child from "./Child";
import { View, StyleSheet } from "react-native";
export default class Parent extends React.Component {
// componentDidMount() {
// setTimeout(() => {
// this.two.input.focus();
// }, 3000);
// }
render() {
return (
<View style={styles.container}>
<Child
destinationRef={() => {
if (!this.two) return this.two;
}}
ref={input => {
this.one = input;
}}
/>
<Child ref={input => (this.two = input)} />
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
alignItems: "center",
justifyContent: "center"
}
});
Note that when I uncomment the componendDidMount, second TextInput successfully focuses three seconds after mounting.
I think the problem in render update. In first render time destinationRef is undefined, parent state not updated or force updated so props not updated too.
I try little bit optimize your code. You can use arrow function to bind this:
import React from "react";
import Child from "./Child";
import { View, StyleSheet } from "react-native";
export default class Parent extends React.Component {
_makeFocus(){
this.two.input.focus();
}
handleOnSubmit(){
this._makeFocus();
}
render() {
return (
<View style={styles.container}>
<Child
onSubmit={this.handleOnSubmit.bind(this)}
ref={input => {
this.one = input;
}}
/>
<Child ref={input => (this.two = input)} />
</View>
);
}
}
I would change the logic a little bit since I think the ref is giving you problems.
Parent:
<View style={styles.container}>
<Child next={this.two} ref={comp => this.one = comp}/>
<Child next={this.one} ref={comp => this.two = comp}/>
</View>
Child:
<TextInput
placeholder='enter text'
onSubmitEditing={() => {
if (this.props.next)
this.props.next.focus();
}}
/>
EDIT:
Your approach was correct I believe, I would just update the parent into this:
export default class Parent extends React.Component {
constructor(props){
super(props);
this.references = {};
}
onFocus = (ref) => {
this.references[ref].focus();
}
render() {
return (
<View style={styles.container}>
<Child
destinationRef={'two'}
onFocus={this.onFocus}
ref={input => this.references['one'] = input}
/>
<Child ref={input => this.references['two'] = input} />
</View>
);
}
}
and your child to:
export default class Child extends React.Component {
render() {
return (
<View>
<TextInput
placeholder='enter text'
onSubmitEditing={() => {
this.props.onFocus(this.props.destinationRef);
}}
/>
</View>
);
}
}
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.
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>
)
}
}
}
I click a button on the navigator, the current screen's one TextInput's is focus.
I need to set the TextInput's instance blur to trigger the textChange function to do something.
In my case, the cursor of TextInput is blinking all the time.
refs.username.blur() does not work.
My code :
class UploadRecordII extends Component{
static navigationOptions = ({ navigation, screenProps }) => ({
headerRight: (<Button containerStyle={{marginRight: 12,}} onPress={ UploadRecordII.onSubmit}>
<Text style={{color: 'white',fontSize: 18,}}>next</Text>
</Button>),
});
.....
static onSubmit() {
let that = UploadRecordII.instance;
debugger;
that.refs.username.blur();
debugger;
that.props.commitCreateRecordUpdate(that.refs.username._lastNativeText,that.imageList)
that.props.navigation.navigate('Record3');
}
...... in render(){
....
<TextInput ref = 'username' placeholder='' editable = {true} maxLength = {500}
keyboardType='default' multiline={true}
style={styles.input} underlineColorAndroid='rgba(0,0,0,0)'
onBlur={ ()=> this.textChange()}
clearButtonMode='always' placeholderTextColor='#D6D0D8'></TextInput>
...
}
refs don't work as strings anymore as expected. You need to define your ref e.g.
class UploadRecordII extends Component {
constructor(props) {
super(props);
this.usernameInput = null;
}
onSubmit() {
this.usernameInput.blur();
}
render() {
return (
<TextInput ref={(input) => this.usernameInput = input} ... />
)
}
}