I am completely new in React Native and I am trying to improve my self with some making sample apps. For this scenario, I only want that when the Button is pressed, the state and the value of the toggle change, such as CLOSED and OPEN. And Make Open is not changed. Now, I have three class components. I am not going to give App's class. I've alread completed it. In this class, I have added a prop:
class A extends Component
{
constructor(props)
{
super(props);
this.state = {change: true};
}
render(){
return(
<View>
<View ...
<Text>CLOSED</Text>
</View>
</View>
);
}
}
Here is my second class B:
class B extends Component
{
state = {isOpen : false};
render()
{
onPress = () => {
...
})
}
return(
<View>
<View ...
<A/>
<Button
title="Make Open"
onPress={this.onPress}
/>
</View>
</View>
);
}
}
I am struggling with the prop. My question is that I want to pass the isOpen value from the class B's state to class A by use of this prop. I think that I could not create state in class B. What is the easiest way to solve this problem ? If I gave not enough information, sorry for that.
When you call component A from component B, you have to pass your state like so:
<A isOpen={this.state.isOpen} />
Then in component A, you can simply access it with props.isOpen.
Related
I want to define another modal to my add modal but I get JSX expressions must have one parent element.
export default class AddModal extends Component {
constructor(props) {
super(props);
this.state = {
};
}
showAddModal = () => {
this.refs.myModal.open();
}
render() {
return(
<Modal ref={"myModal"}
style={{ ...
</Modal>
// till this part everything is ok
<Modal ref={"anotherModal"}> // I want to this but I can't
As the error states, you must have only one parent element inside the render() method.
Just wrap everything under a View component, so your render() method would look like:
render() {
return (
<View>
<Modal ref={"myModal"}>
</Modal>
<Modal ref={"anotherModal"}>
</Modal>
</View>
);
}
Use React.Fragment
https://reactjs.org/docs/fragments.html
Something like this (short syntax example)
<>
<Modal ref={"myModal"}></Modal>
<Modal ref={"anotherModal"}></Modal>
</>
On pressing the button(ButtonComponent), the state of the button gets changed. Now I'm passing that state to the <FlatListItem> (a child component ) inside <FlatList>. Depending on that status, the each item in the <FlatList> should be re-arranged.
I just got to know about this extraData prop but not sure how to make use of it in the code.
It's not something new but it is like simple checkbox implementation.
Everything is working fine but when I press the select all button all the remaining select buttons are not getting toggled to selected.
class FlatListItem extends Component{
constructor(props){
super(props)
const{ isSelected }=this.props
this.state={
selectedStatus:isSelected,
}
}
changeSelectStatus=(key)=>{
this.setState({selectedStatus:!this.state.selectedStatus});
return key;
}
render(){
return(
<View style={{flex:1,
flexDirection:'row',
backgroundColor:'white'}}>
<View>
<Image
source={{uri:this.props.item.imageUri}}
style={{width:50, height:50, margin:5}}>
</Image>
</View>
<View>
<Text style={{color:'black', padding:10, fontSize:16}}>{this.props.item.name}</Text>
</View>
<View style={{flex:1, alignItems:'flex-end', paddingRight:-10}}>
{this.state.selectedStatus?
**<ButtonComponent buttonColor={"black"} buttonTextColor={"white"} fullRounded={true}
borderHighlight={true} buttonWidth={70} buttonHeight={30}
onPress={()=>this.props.showSelected(this.changeSelectStatus(this.props.item.key)) }>
Selected
</ButtonComponent>
:
<ButtonComponent buttonColor={"white"} buttonTextColor={"black"} fullRounded={true}
borderHighlight={true} buttonWidth={70} buttonHeight={30}
onPress={()=>this.props.showSelected(this.changeSelectStatus(this.props.item.key)) }>
Select
</ButtonComponent>
}
</View>
</View>
)
}
}
export default class SelectMembersBody extends Component {
constructor(props){
super(props)
this.state={
selectedButtons:[],
selectAllBtnStatus:false,
}
}
selectAllMembers=()=>{
let allMembers=[];
if(!this.state.selectAllBtnStatus){
membersData.forEach(element => {
if(!this.state.selectedButtons.includes(element.key))
allMembers.push(element.key)
});
this.setState({
selectAllBtnStatus:!this.state.selectAllBtnStatus,
selectedButtons:[...this.state.selectedButtons, allMembers]
})
}
else{
this.setState({
selectAllBtnStatus:!this.state.selectAllBtnStatus,
selectedButtons:[...allMembers]
})
}
}
showSelected=(callback)=>{
let val = callback;
if(!this.state.selectedButtons.includes(val))
this.setState({selectedButtons:[...this.state.selectedButtons, val]});
else{
let newMarkers=[...this.state.selectedButtons]
let index = newMarkers.indexOf(val);
if (index >= 0) {
newMarkers.splice( index, 1 );
}
this.setState({selectedButtons:newMarkers});
}
}
render(){
return(
<View style={{flex:1, }}>
<Text>{this.state.selectedButtons}</Text>
<View>
{this.state.selectAllBtnStatus?
<ButtonComponent buttonColor={"black"} buttonTextColor={"white"} fullRounded={true}
borderHighlight={true} buttonWidth={85} buttonHeight={30} onPress={this.selectAllMembers}>
Selected All
</ButtonComponent>
:
<ButtonComponent buttonColor={"white"} buttonTextColor={"black"} fullRounded={true}
borderHighlight={true} buttonWidth={85} buttonHeight={30} onPress={this.selectAllMembers}>
Select All
</ButtonComponent> }
</View>
<FlatList data={membersData} extraData={this.state}
renderItem={({item, index})=>{
return(
<View>
<FlatListItem item={item} index={index} isSelected={this.state.selectAllBtnStatus} showSelected={this.showSelected} ></FlatListItem>
</View>
)
}
}></FlatList>
</View>
)
}
}
I know that it's quite harder to step into someone's shoes. The code I provided in my question might not be that effective since I'm a learner. That's why it is uncomfortable to get on to the code flow. So, I decided to answer my own question after reading some articles, docs and similar questions on StackOverFlow.
So, my question is why the flatlist was not getting re-rendered on setState. To be more precise, if you've gone through the pictures above, on selecting the Select All button all the buttons in the items should be toggled to selected state.
To make the flatlist re-render, we need to add an additional prop "extraData"
By passing extraData={this.state} to FlatList we make sure FlatList itself will re-render when the state.selected changes. Without setting this prop, FlatList would not know it needs to re-render any items because it is also a PureComponent and the prop comparison will not show any changes.
You can get it's full documentation here.
In my case, I set extraData = {this.state} since the status of buttons in each of the flatlist item depends on the array which is present in the parent component.
So, I've sent it to child component via props.
Now the flatlist is working fine and re-rendering on every state update.
I get a problem to get new data in parent component (Profile) after update data in child component (ProfileEdit), here I create a simple script in order easy to understand, default component is Profile, why cannot show alert in Profile after back from ProfileEdit, please give advices or correct my script how to show alert after back from ProfileEdit.
Profile.js
export default class Profile extends Component {
componentDidMount() {
alert('Success');
}
toProfileEdit() {
this.props.navigation.navigate('ProfileEdit');
}
render() {
return (
<View>
<Button
onPress={() => this.toProfileEdit()}
title="Learn More" />
</View>
)
}
}
ProfileEdit.js
export default class ProfileEdit extends Component {
backTo() {
this.props.navigation.navigate('Profile');
}
render() {
return (
<View>
<Button
onPress={() => this.backTo()}
title="Learn More" />
</View>
)
}
}
Please anyone help me to solve this problem.
Thanks.
Profile already mount then componentWillMount will not call again on the back action. You can pass the function with prop action.
export default class ProfileEdit extends Component {
backTo() {
this.props.navigation.navigate('Profile');
}
render() {
return (
<View>
<Button
onPress={() => { this.props.something(); this.backTo()}}
title="Learn More" />
</View>
)
}
}
Pass function which name is something, call it after you can go back.
on clicking the text, i get an error saying "undefined is not an object (evaluating '_this2.categoryClicked.bind')"
I think the error is "onPress={()=>this.categoryClicked.bind(this)}" there must be a different way to call the categoryClicked function when the button is clicked. What is wrong in my code ?
class CategoriesView extends React.Component {
constructor(props){
super(props)
}
categoryClicked(){
this.props.categoryPressed(this.props.Category);
}
renderSubCategory(){
return(
this.props.Category.sub_category.map(function(subCategory, i){
return(
<View style={styles.abcd}>
<TouchableHighlight onPress={()=>this.categoryClicked.bind(this)}>
<Text>{subCategory.title}</Text>
</TouchableHighlight>
</View>
)
})
)
}
render(){
return(
<View style={{flex:1}}>
<View style={styles.avf}>
<Text>{this.props.Category.heading}</Text>
</View>
<View style={styles.ddd}>
{this.renderSubCategory()}
</View>
</View>
)
}
}
I believe what you want to do is onPress={this.categoryClicked.bind(this)} instead of an arrow function. .bind(this) returns a function with the context correctly binded to this, therefore, it does not get invoked.
Also, I suggest putting the binding in constructor, as you don't want the binding to happen every time the component re-renders.
e.g.
constructor(props) {
super(props);
this.categoryClicked = this.categoryClicked.bind(this);
}
Then just use onPress={this.categoryClicked}
If you want to pass down sub-category, you can do
constructor(props) {
super(props);
this.subcategoryClicked = props.Category.sub_categories.map(sub_category => this.categoryClicked.bind(this, sub_category));
}
then use like this in render:
this.props.Category.sub_category.map(function(subCategory, i) {
<View style={styles.abcd}>
<TouchableHighlight onPress={this.subcategoryClicked[i]}>
<Text>{subCategory.title}</Text>
</TouchableHighlight>
</View>
P.S, I am not sure if this is a good pattern to follow. Stick to this.categoryClicked(bind, subcategory) if you are not comfortable with doing this. This is one of those things that I am not sure if the optimization is worth it.
this in onPress={()=>this.categoryClicked.bind(this)}> points to sub_category.map function. It should instead point to the class. Can be done this way instead
return (
this.props.Category.sub_category.map((subCategory, i) => { // <--- this way context is preserved
// other lines
onPress={()=>this.categoryClicked.bind(this, subCategory)}>
// other lines
})
);
in categoryClicked method should be accessible
categoryClicked(category){
this.props.categoryPressed(category);
}
Here is what I am using:
<Modal
visible = {this.props.visible}
animationType="slide"
transparent
onRequestClose={() => {}} >
<TextInput
style = {styles.inputBox}
ref = {this.props.destinatinon} />
</Modal>
and in the Container
<ExampleModal
destination = {this.state.destination} >
</ExampleModal>
I don't know how to pass data from Modal to Parent Component. Any kind of Tutorial or link is fine. Thanks in Advance.
Let's assume that your Modal is filed separately in /components/MyModal to generalize things.
You can make your Modal call a function that you passed by props every time input text is changed. Here's a simple callback logic you can use.
Avoid using refs as much as you can.
import MyModal from '../components/MyModal';
...
class Home extends Component {
onInputChanged = (changedText) => {
console.log('This is the changed text: ', changedText);
}
render() {
return (
<View>
...
<MyModal onInputChanged={this.onInputChanged} .../>
</View>
)
}
}
// components folder
class MyModal extends Component {
render() {
return (
<Modal
visible = {this.props.visible}
animationType="slide"
transparent
onRequestClose={() => {}} >
<TextInput
style = {styles.inputBox}
onChangeText={(changedText) => this.props.onInputChanged(changedText)} />
</Modal>
)
}
}
Side Note: You can define MyModal stateless to make things a bit cleaner.