How to pass changing state prop to component? - javascript

I have a state object called isEdit.
And I have a list of StyledLessons (a component with functionallity that should differ if its in Edit Mode or not).
<StyledLesson title={title} subjectName={subjectName} subtitle={subtitle} isEdit={this.state.isEdit}/>
It's working fine with the default state (so I do see the expected result in the beginning if isEdit is true or false as expected) but the content doesn't change if I change my state in the parent object.
Any ideas how I could manage the child component to change on the fly?
Here is how I change my state:
this.state.isEdit ?
<Icon
name='check'
color={Colors.tabBarText}
onPress={() => this.setState({isEdit: false})}
/>
:
<Icon
name='edit'
color={Colors.tabBarText}
onPress={() => this.setState({isEdit: true})}
/>
Code of the child component:
class StyledLesson extends React.PureComponent {
constructor(props) {
super(props);
this.isEdit = props.isEdit??true;
this.state = {expanded: false};
}
getEditableView() {
return (
<View style={{flexDirection: 'row', flex: 1, alignItems: 'center', marginHorizontal: 10}}>
<TouchableOpacity>
<Icon
name={'remove-circle'}
color={'#f00'}
/>
</TouchableOpacity>
<TouchableOpacity style={{flex: 1}}>
<FlatCard>
<Text style={{color: '#666', fontSize: 18}}>{this.props.title}</Text>
<Text
style={{
fontWeight: 'bold',
fontSize: 35,
color: Colors.textColor
}}>{this.props.subjectName}</Text>
<Text style={{color: '#666', fontSize: 18}}>{this.props.subtitle}</Text>
</FlatCard>
</TouchableOpacity>
</View>
);
}
getDisplayView() {
return (
//Normal View
<View style={{flexDirection: 'row', flex: 1, alignItems: 'center', marginHorizontal: 10}}>
<TouchableOpacity
onPress={
() => {
this.setState({expanded: !this.state.expanded});
}
}
style={{flex: 1}}
>
<FlatCard>
{this.state.expanded &&
<Text style={{color: '#666', fontSize: 18}}>{this.props.title}</Text>
}
<Text
style={{
fontWeight: 'bold',
fontSize: 35,
color: Colors.textColor
}}>{this.props.subjectName}</Text>
{this.state.expanded &&
<Text style={{color: '#666', fontSize: 18}}>{this.props.subtitle}</Text>
}
</FlatCard>
</TouchableOpacity>
</View>
);
}
render() {
return (
this.isEdit ? this.getEditableView() : this.getDisplayView()
);
}
}
export default StyledLesson;

this.isEdit = props.isEdit??true;
This is the issue. You are updating this property only inside the constructor. Event if the props value is changing, the property won't.
You can try to modify your render method to something like
render() {
return (
(this.props.isEdit??true) ? this.getEditableView() : this.getDisplayView()
);
}

Related

(React Native) Undefined is not an object (evaluating 'navigation.navigate')

From App.js, I declare the "FollowingScreen", which is made of a module that exports "Following"
export const FollowingScreen = ({route, navigation}) => {
return (
<ScrollView style={styles.scrollView}>
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: "#262423" }}>
<Following />
</View>
</ScrollView>
);
}
"Following" is exported by a file called "following.js". From "following.js" I want to navigate to ProfileScreen:
import { useNavigation } from '#react-navigation/native';
class Following extends Component {
...
...
renderItem = ({item}, navigation) => (
<ListItem bottomDivider>
<ListItem.Content style={{width: '100%', display: 'flex', flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center'}}>
<ListItem.Title>{item.title}</ListItem.Title>
<TouchableOpacity
onPress={() => navigation.navigate("ProfileScreen", {userInfo: {uri: item.probability, username: item.country_id, id_user: item.id_user}})}
style={{ flexDirection: 'row'}}
>
<Image
style={{ borderRadius: 50 }}
source={{ uri: item.probability, width: 48, height: 48 }}
/>
</TouchableOpacity>
<TouchableOpacity
onPress={() => navigation.navigate("ProfileScreen", {userInfo: {uri: item.probability, username: item.country_id, id_user: item.id_user}})}
style={{ flexDirection: 'row'}}
>
<ListItem.Subtitle style={{color: '#000'}}>
<Text style={{fontFamily: 'Montserrat_600SemiBold' }}>{item.name} {item.surname}</Text>
{"\n"}
<Text style={{fontFamily: 'Montserrat_600SemiBold' }}>#{item.country_id}</Text>
{"\n"}
{"\n"}
<Text style={{fontFamily: 'Montserrat_600SemiBold' }}>{item.followers}</Text>
{"\n"}
<Text style={{fontFamily: 'Montserrat_600SemiBold' }}>{item.total}</Text> Total
</ListItem.Subtitle>
</TouchableOpacity>
<Button
buttonStyle={{backgroundColor: "#a6aba7", padding: 9, textAlign: "right", borderRadius: 10, display: item.isFollowing=="Same user" ? "none" : "flex"}}
title={item.isFollowing}
onPress={() => {
if(item.isFollowing=="Follow"){
this.follow(item.id_user);
}
else if(item.isFollowing=="Unfollow"){
this.unfollow(item.id_user);
}
else if(item.isFollowing=="Same user"){
//alert("Same user");
}
}}
/>
</ListItem.Content>
</ListItem>
);
}
unfortunately, I get "undefined is not an object (evaluating 'navigation.navigate')"
You are not passing navigation as a prop to the Following component so change your FollowingScreen component likewise :
export const FollowingScreen = ({route, navigation}) => {
{/** Rest Of Code **/}
<Following navigation={navigation}/>
{/** Rest of Code **/}
}
Then in Following class use this.props.navigation.navigate(....).
You need to declare "navigation" before calling it:
const navigation = useNavigation();
right before
renderItem = (...)

How to rerender item by clicking on it expo js?

I have a flatlist with rendered list of items. I need for each item to change circle to checked-circle on click and go back on second click. I tried to do it with changing item prop isChecked but circle doesn't rerender until I go out of the screen and came back. How may I fix my problem?
Here is my rendering code:
const renderItems = ({ item }) => (
<TouchableOpacity
onPress={() => (item.isChecked = !item.isChecked)}
style={
item.isMarked
? {
backgroundColor: '#FE2C55',
flexDirection: 'row',
alignItems: 'center',
height: word_height
}
: { flexDirection: 'row', alignItems: 'center', height: word_height }
}
>
<View
style={{
width: 45,
justifyContent: 'center',
alignItems: 'center',
height: '100%',
backgroundColor: '#F6FFE0'
}}
>
{item.isChecked ? (
<Feather name="check-circle" size={25} color="#3D5201" />
) : (
<Feather name="circle" size={25} color="#3D5201" />
)}
</View>
<View style={styles.word_line}>
<View style={{ width: word_width, paddingLeft: 20 }}>
<Text style={{ fontSize: 20 }}>{item.word}</Text>
</View>
<View style={{ width: word_width, paddingLeft: 20 }}>
<Text style={{ fontSize: 20 }}>{item.translation}</Text>
</View>
</View>
</TouchableOpacity>
);
On this line, you are trying to mutate the props variable, so react will not update the component when the props state is mutated.
onPress={() => (item.isChecked = !item.isChecked)}
To do this, create a separate component (based on the name of the renderItems method, I guess it's just a function in the component) and add state to it.
Use useState and the initial state value that was taken from the props initialIsChecked
const Item = ({isMarked, isChecked: initialIsChecked, word, translation}) => {
const [isChecked, setIsChecked] = useState(initialIsChecked);
const handleClick = () => {
setIsChecked((prev) => !prev);
};
return (
<TouchableOpacity
onPress={handleClick}
style={
isMarked
? {
backgroundColor: '#FE2C55',
flexDirection: 'row',
alignItems: 'center',
height: word_height,
}
: {flexDirection: 'row', alignItems: 'center', height: word_height}
}>
<View
style={{
width: 45,
justifyContent: 'center',
alignItems: 'center',
height: '100%',
backgroundColor: '#F6FFE0',
}}>
{isChecked ? (
<Feather name="check-circle" size={25} color="#3D5201" />
) : (
<Feather name="circle" size={25} color="#3D5201" />
)}
</View>
<View style={styles.word_line}>
<View style={{width: word_width, paddingLeft: 20}}>
<Text style={{fontSize: 20}}>{word}</Text>
</View>
<View style={{width: word_width, paddingLeft: 20}}>
<Text style={{fontSize: 20}}>{translation}</Text>
</View>
</View>
</TouchableOpacity>
);
};
Then the list component should look something like this:
const List = () => {
return <ScrollView>
items.map((item) => (
<Item
key={item.id} // if there is id
isMarked={item.isMarked}
isChecked={item.isChecked}
word={item.word}
translation={item.translation}
/>
)
</ScrollView>;
};

react native : there is way to pass a function into "onpress"?

there is way to pass a function into "onpress" ?
i need to pass the "postData" function into the "onpress" button ,
how can i do it?
in my code the has 2 "onpress" that i want to pass inside the "postData" .
if there some mistake so please let me know and i will fix it .
this is my code for example :
export default class OrderInformationScreen extends Component {
constructor(props) {
super(props);
const { state } = props.navigation;
this.state = {
title: state.params.data
}
//alert(JSON.stringify((state.params.data.SHORT_TEXT)))
}
postData = () => {
const postData = {
ACTOR_ID:"APAZ",
REPORT_KEY:"001",
WORK_ITEM_ID:"000018639250",
NOTE:"fun all time"
}
const axios = require('axios')
axios.post('https://harigotphat1.mekorot.co.il/ConfirmPackaotWS/OrderApprove/OrderApprove_OrderApp_Save_Approvement/'+ postData)
.then(function (response) {
console.log("roei response======>>>>",response);
})
}
render() {
return (
<>
<View
style={{
alignItems: 'flex-start',
justifyContent: 'center',
borderColor: 'blue',
flexDirection: "row",
justifyContent: 'space-evenly'
}}>
<TouchableOpacity onPress={() => console.log("cancel!")}>
<Avatar
size='large'
containerStyle={{ marginTop: 30 }}
activeOpacity={0.2}
rounded
source={require('../assets/down.png')} style={{ height: 80, width: 80 }}
onPress={() => console.log("cancel!")} />
<View >
<Text style={{ fontSize: 25, fontWeight: 'bold', color: 'red' }}>לדחות</Text>
</View>
</TouchableOpacity>
<TouchableOpacity onPress={() => console.log("works!")}> ///HERE I NEED PASS postData
<Avatar
size='large'
activeOpacity={0.1}
rounded
source={require('../assets/up.png')} style={{ height: 80, width: 80 }}
onPress={() => console.log("Works!")} />
<View>
<Text style={{ fontSize: 25, fontWeight: 'bold', color: 'green', marginHorizontal: 6 }}>לאשר</Text>
</View>
</TouchableOpacity>
</View>
<InfoTable headerInfo={this.state.title}></InfoTable>
</>
);
};
}
Check this updated code
export default class OrderInformationScreen extends Component {
constructor(props) {
super(props);
const { state } = props.navigation;
this.state = {
title: state.params.data
};
//alert(JSON.stringify((state.params.data.SHORT_TEXT)))
}
postData = () => {
const postData = {
ACTOR_ID: "APAZ",
REPORT_KEY: "001",
WORK_ITEM_ID: "000018639250",
NOTE: "fun all time"
};
const axios = require("axios");
axios
.post(
"https://harigotphat1.mekorot.co.il/ConfirmPackaotWS/OrderApprove/OrderApprove_OrderApp_Save_Approvement/" +
postData
)
.then(function(response) {
console.log("roei response======>>>>", response);
});
};
render() {
return (
<>
<View
style={{
alignItems: "flex-start",
justifyContent: "center",
borderColor: "blue",
flexDirection: "row",
justifyContent: "space-evenly"
}}
>
<TouchableOpacity onPress={() => console.log("cancel!")}>
<Avatar
size="large"
containerStyle={{ marginTop: 30 }}
activeOpacity={0.2}
rounded
source={require("../assets/down.png")}
style={{ height: 80, width: 80 }}
onPress={() => console.log("cancel!")}
/>
<View>
<Text style={{ fontSize: 25, fontWeight: "bold", color: "red" }}>
לדחות
</Text>
</View>
</TouchableOpacity>
<TouchableOpacity onPress={() => this.postData()}>
<Avatar
size="large"
activeOpacity={0.1}
rounded
source={require("../assets/up.png")}
style={{ height: 80, width: 80 }}
onPress={() => console.log("Works!")}
/>
<View>
<Text
style={{
fontSize: 25,
fontWeight: "bold",
color: "green",
marginHorizontal: 6
}}
>
לאשר
</Text>
</View>
</TouchableOpacity>
</View>
<InfoTable headerInfo={this.state.title}></InfoTable>
</>
);
}
}
Simply you can put function like this way
<TouchableOpacity onPress={() => this.postData()}> .... </TouchableOpacity>

how To call function on particular response in react native

Below object response I am getting , on FAILURE response I am trying to call function that open modal .
But I guess its wrong approach, please correct me . If I call same function button click then Modal is coming . Please how can I open modal on failure and success I'll got o next page screen .
Please suggest me.
/// Below response //////////
Object response
validateUserResponse:
reference_id: "NxNxNxNxNxNxNxNxNxNxNxN"
status: "FAILURE"
/////////////////////////
this.state={
isModalVisible: false,
}
_toggleModal = () =>
this.setState({ isModalVisible: !this.state.isModalVisible });
render(){
const {validateUserResponse} = this.props;
if (!_.isEmpty(validateUserResponse)) {
validateUserResponse.status==='FAILURE'? this._toggleModal():LoadScreen.bind(that, 2, validateUserResponse)();
}
return (
<View>
<Modal
isVisible={this.state.isModalVisible}
onBackButtonPress={() => this._toggleModal()}
onBackdropPress={() => this._toggleModal()}
style={{ margin: 0 }}
>
<View style={style.modalContainer}>
<View style={style.innerContainer}>
<View style={style.detailsContainer}>
<View style={{ flexDirection: 'column', alignItems: 'flex-end', marginTop: -40 }}>
<TouchableOpacity onPress={() => this._toggleModal()} >
<Image source={CLOSE_W} />
</TouchableOpacity>
</View>
<View style={{ flexDirection: 'column', justifyContent: 'center' }}>
<View style={{ flexDirection: 'row', justifyContent: 'center', marginTop: 10, alignContent: 'center', alignItems: 'center', alignSelf: 'center' }}>
<IconSmall icon="report-problem" type="MaterialIcons" style={{ color: 'red', paddingRight: 5, paddingBottom: 3 }} />
<Text>CUSTOMER E-VALIDATION FAILED</Text>
</View>
<View style={{
justifyContent: 'center',
height: 2,
margin: 5,
borderBottomWidth: 1,
borderBottomColor: 'grey',
paddingBottom: 10
}} />
</View>
<View style={{ flexDirection: 'column', marginBottom: 10 }}>
<RegularText text={'Status'} textColor='grey' style={{ marginBottom: 5 }} />
<SmallText text={validateUserResponse.status} textColor='red' />
</View>
<View style={{ padding: 10 }}>
<PrimaryBtn label={'Validate Again'} disabled={false}
onPress={() => this._toggleModal()} />
</View>
<View style={{ padding: 10 }}>
<YellowBtn label={'Go to Dashboard'} disabled={false}
onPress={() => this._gotoDashboard()} />
</View>
</View>
</View>
</View>
</Modal>
</View>
)
Thanks
You should not change the state in render method . "_toggleModal" is method which change state and it is bad approach.
You should use getderivedstatefromprops
getDerivedStateFromProps is invoked right before calling the render
method, both on the initial mount and on subsequent updates. It should
return an object to update the state, or null to update nothing.
static getDerivedStateFromProps(nextProps, prevState){
if(!_.isEmpty(nextProps.validateUserResponse) && nextProps.validateUserResponse.status==='FAILURE'){
return { isModalVisible: true};
}
else return null;
}

How to send and display the value of a text input on another screen with native react?

I want to send the data of a text input that is stored in a variable to another screen and show the data that was saved in said variable
Try to pass the state of the variable by react-navigation but the problem is that I have 3 screens and I want to pass the data stored in the variable text from screen 1 to screen 3
class Screen1 extends Component {
constructor(props) {
super(props);
this.state = {
text: '',
percentage: ''
};
}
static navigationOptions = ({ navigation }) => {
const { params = {} } = navigation.state
var data = {
textData: params.text
}
return {
headerLeft: (
<View style={{ flexDirection: 'row', justifyContent: 'center', alignItems: 'center' }}>
<AntDesign
name="left"
color="rgba(0,0,0,0.5)"
size={30}
style={{ marginLeft: 5 }}
/>
<Text style={{ marginLeft: 20, color: '#000', fontSize: 17, fontWeight: 'bold' }}>Crear regalo</Text>
</View>
),
headerRight: (
<View>
<TouchableOpacity
disabled={navigation.getParam('isDisable')} // get value from params and pass it to disabled props
onPress={() => navigation.navigate('2', { data })}
style={styles.Btn}>
<Text style={styles.TxtBtn}>Siguiente</Text>
</TouchableOpacity>
</View>
),
}
};
// set by a default value in componentDidMount to make the next button disable initially
componentDidMount() {
Alert.alert('Advertencia', 'Ingrese el porcentaje de su descuento');
this.props.navigation.setParams({ isDisable: true });
}
render() {
return (
<View style={styles.container}>
<View style={styles.Box}>
<ImageBackground source={require('../Icons/Regalo.png')} style={styles.Image}>
<View style={styles.ContainerInput}>
<TextInput
style={{ textAlign: 'center', color: '#fff', fontSize: 40, }}
type="numeric"
placeholder="%"
value={this.state.text} //set value from state
onChangeText={(text) => {
//when text length is greater than 0 than next button active otherwise it will be disable
let isDisable = text.length > 0 ? false : true
//set value in the state
this.setState({ text: text })
// set value to params
this.props.navigation.setParams({ isDisable: isDisable });
}} />
<Text style={{ fontSize: 40, color: '#fff', textAlign: 'center' }}>
{this.state.text.split(' ').map((word) => word && '%').join(' ')}
</Text>
</View>
</ImageBackground>
</View>
</View>
);
}
}
export default Screen1;
My Screen Two:
class Screen2 extends Component {
constructor(props) {
super(props);
this.state = {
textData: this.props.navigation.state.params.data.textData,
text: ''
};
}
static navigationOptions = ({ navigation }) => {
const { params = {} } = navigation.state
var data = {
textData: params.textData
}
return {
headerLeft: (
<View style={{ flexDirection: 'row', justifyContent: 'center', alignItems: 'center' }}>
<AntDesign
name="left"
color="rgba(0,0,0,0.5)"
size={30}
style={{ marginLeft: 5 }}
onPress={() => navigation.navigate('1')}
/>
<Text style={{ marginLeft: 20, color: '#000', fontSize: 17, fontWeight: 'bold' }}>Crear regalo</Text>
</View>
),
headerRight: (
<View>
<TouchableOpacity
disabled={navigation.getParam('isDisable')} // get value from params and pass it to disabled props
onPress={() => navigation.navigate('3', { data })}
style={styles.Btn}>
<Text style={styles.TxtBtn}>Crear</Text>
</TouchableOpacity>
</View>
),
}
};
componentDidMount() {
this.props.navigation.setParams({ isDisable: true });
}
render() {
return (
<View style={styles.container}>
<View style={styles.InputContainer}>
<TextInput
multiline
style={styles.Input}
type="numeric"
placeholder="Describa los términos y condificones de tu regalos"
placeholderTextColor="rgb(196,196,196)"
value={this.state.text} //set value from state
onChangeText={(text) => {
//when text length is greater than 0 than next button active otherwise it will be disable
let isDisable = text.length > 1 ? false : true
//set value in the state
this.setState({ text: text })
// set value to params
this.props.navigation.setParams({ isDisable: isDisable });
}} />
</View>
</View>
);
}
}
export default Screen2;
My Screen Three:
class Screen3 extends Component {
constructor(props) {
super(props);
this.state = {
textData: this.props.navigation.state.params.data.textData,
text: '',
percentage: '',
};
}
static navigationOptions = ({ navigation }) => ({
headerLeft: (
<View style={{ flexDirection: 'row', justifyContent: 'center', alignItems: 'center' }}>
<AntDesign
name="left"
color="rgba(0,0,0,0.5)"
size={30}
style={{ marginLeft: 5 }}
onPress={() => navigation.navigate('2')}
/>
<Text style={{ marginLeft: 20, color: '#000', fontSize: 17, fontWeight: 'bold' }}>Crear regalo</Text>
</View>
),
headerRight: (
<View>
<TouchableOpacity
disabled={navigation.getParam('isDisable')} // get value from params and pass it to disabled props
onPress={() => navigation.navigate('4')}
style={styles.Btn}>
<Text style={styles.TxtBtn}>Crear</Text>
</TouchableOpacity>
</View>
),
});
// set by a default value in componentDidMount to make the next button disable initially
componentDidMount() {
this.props.navigation.setParams({ isDisable: true });
}
render() {
let datos = [{
value: 'Banana',
}, {
value: 'Mango',
}, {
value: 'Pear',
}];
var data = {
textData: this.state.textData
}
return (
<View style={styles.container}>
<View style={styles.BoxandInput}>
<View style={styles.ContainerInput}>
<TextInput
style={styles.Input}
multiline
placeholder='Escriba una Descripcion'
placeholderTextColor="rgb(196,196,196)"
maxLength={203}
/>
</View>
<View style={styles.Box}>
<ImageBackground
source={require('../Icons/Regalo.png')}
style={styles.Image}>
<View style={styles.ContainerText}>
<Text style={styles.TextBox}>{data}</Text>
</View>
</ImageBackground>
</View>
</View>
<View style={styles.Regalos}>
<View style={{ justifyContent: 'center', alignItems: 'center', flexDirection: 'row' }}>
<Text style={styles.line}>--------------------------</Text>
<Text style={styles.RegalosText}>Cantidad de Regalos</Text>
<Text style={styles.line}>--------------------------</Text>
</View>
<View style={{ justifyContent: 'center', alignItems: 'center', marginTop: 30 }}>
<TextInput
style={styles.RegalosInput}
placeholder='0'
placeholderTextColor='#000'
maxLength={6}
type='numeric'
/>
<View style={styles.LineInput} />
<Text style={{ fontSize: 10, color: '#ccc', textAlign: 'center', marginTop: 5 }}>60 regalos Disponibles</Text>
<TouchableOpacity style={{ marginTop: 15 }}>
<Text style={{ fontSize: 10, color: 'cyan', textAlign: 'center' }}>Comprar Regalos</Text>
</TouchableOpacity>
</View>
</View>
<View style={styles.ContainerServices}>
<View style={{ justifyContent: 'center', alignItems: 'center', flexDirection: 'row' }}>
<Text style={styles.line}>-----------------------------------</Text>
<Text style={styles.RegalosText}>Servicios</Text>
<Text style={styles.line}>-----------------------------------</Text>
</View>
<Dropdown
dropdownMargins={{ min: 5, max: 10 }}
label='Favorite Fruit'
data={datos}
/>
</View>
</View>
);
}
}
export default
This Problem
You can create a data object and you can send it with navigation to next UI. You can pass the data object from UI 1 to UI 2 and then you can send it UI 3 from UI 2.
In the first UI you can create data variable like,
var data = {
textData: text
}
Include the above code inside navigationOptions of your code.
Then in the navigation call include the data object as well.
onPress={() => navigation.navigate('2', {data})}
In the second UI assign the passed value to a variable inside the constructor.
constructor(props) {
super(props);
this.state = {
textData: this.props.navigation.state.params.data. textData,
}
}
Then when you are going to navigate to the third UI create a data variable and send it as previously done.
var data = {
textData: this.state.textData
}
Pass this variable with navigate.navigation and access it from third UI similar as in the second UI.

Categories

Resources