Change useState's from remote component - javascript

I know with the following code below changing a useState within another component's onPress event wouldn't be possible but how would I do it? I want to make it so when the onPress function within Card.js is executed the popUpData from within App.js is changed.
App.js
const [popUpData, setPopUpData] = React.useState("Nothing")
return (
<Card>
<Text style={styles.pokemonName}>{item.name}</Text>
</Card>
<Text>{popUpData}</Text>
)
Card.js
const doSomething = () => {
setPopUpData("Something")
//other things...
}
return (
<TouchableOpacity activeOpacity={1} style={styles.card} onPress={doSomething()}>
<View style={styles.cardContent}>
{ props.children }
</View>
</TouchableOpacity>
)

App.js
const [popUpData, setPopUpData] = React.useState("Nothing")
return (
<Card onUpdate={(d)=>setPopUpData(d) }>
<Text style={styles.pokemonName}>{item.name}</Text>
</Card>
<Text>{popUpData}</Text>
)
Card.js
function Card(props){
const doSomething = () => {
props.onUpdate("Something")
}
return (
<TouchableOpacity activeOpacity={1} style={styles.card} onPress={doSomething()}>
<View style={styles.cardContent}>
{ props.children }
</View>
</TouchableOpacity>
)
}

Just pass reference to setState function down to child component.
(in the simplest scenario)
const Parent = () => {
const [popUpData, setPopUpData] = React.useState("Nothing")
return (
<Card setPopUpDataHandler={setPopUpData}>
<Text style={styles.pokemonName}>{item.name}</Text>
</Card>
<Text>{popUpData}</Text>
)
}
const Card = ({setPopUpDataHandler, children}) => {
const doSomething = () => {
setPopUpDataHandler("Something")
//other things...
}
return (
<TouchableOpacity activeOpacity={1} style={styles.card} onPress={doSomething()}>
<View style={styles.cardContent}>
{ children }
</View>
</TouchableOpacity>
)
}

Related

Changing style of specific component returned from map function onClick

I am trying to change the style of individual TouchableOpacity components that have been returned from a map function.
Here is the component:
Example = ({ props }) => {
return (
<View>
{props.listExample.map(({ id }) => {
return (
<React.Fragment key={id}>
<TouchableOpacity
style={styles.button}
onPress={() => console.log(id)}>
<Image source={require('example.jpg')} />
</TouchableOpacity>
</React.Fragment>
);
})}
</View>
);
};
Let TouchableOpacity = TO.
The map function returns about 30 TOs with unique IDs. When I click the TOs, I can see their unique ID in the console log. I want to know how I can modify the style of an individual TO.
Here is my render function which uses the functional component Example.
render() {
return (
<View style={styles.body}>
<ScrollView>
<View style={styles.column}>
<this.Example props={{ listExample: this.getList() }} />
</View>
</ScrollView>
</View>
);
}
What I have tried:
referencing this stackoverflow post, I tried to create a function which changed the style of the TO when it is clicked. But the result of this changed all the TOs in the UI since of the way it is mapped.
I tried something like the following.
Example = ({ props }) => {
return (
<View>
{props.listExample.map(({ id }) => {
let buttonStyle = this.state.pressed ? styles.button : styles.buttonClicked
return (
<React.Fragment key={id}>
<TouchableOpacity
style={buttonStyle}
onPress={() => console.log(id)}>
<Image source={require('example.jpg')} />
</TouchableOpacity>
</React.Fragment>
);
})}
</View>
);
};
But as previously stated, this changed all of the Touchable Opacitys. Is there a way to only change one?
Thanks
Edit - to show entire class
class Page extends Component {
constructor(props) {
super(props)
}
MyButton = ({ onButtonPressed = () => {} }) => {
const [isPressed, setIsPressed] = useState(false);
const onPressed = () => {
setIsPressed(!isPressed);
onButtonPressed();
}
return (<TouchableOpacity style={isPressed ? styles.pressedButton: styles.button}
onPress={onPressed}>
<Image source={require('example.jpg')} />
</TouchableOpacity>
);
}
Example = ({ props }) => {
return (
<View>
{props.listExample.map(({ id }) => {
return (
<MyButton key={id}/>
);
})}
</View>
);
};
render() {
return (
<View style={styles.body}>
<ScrollView>
<View style={styles.column}>
<this.Example props={{ listExample: this.getList()}} />
</View>
</ScrollView>
</View>
);
}
}
It is easier to separate the component inside map to a separate component and then handle style changes on press there
const MyButton = ({ onButtonPressed = () => {} }) => {
const [isPressed, setIsPressed] = useState(false);
const onPressed = () => {
setIsPressed(!isPressed);
onButtonPressed();
}
return (<TouchableOpacity style={isPressed ? styles.pressedButton: styles.button}
onPress={onPressed}>
<Image source={require('example.jpg')} />
</TouchableOpacity>
)
}
so you can use in the map like this
Example = ({ props }) => {
return (
<View>
{props.listExample.map(({ id }) => {
return (
<MyButton key={id} />
);
})}
</View>
);
};

Bind a object value into a component when TouchableOpacity Avatar pressed

My goal is to display the overlay bound to that button when the baby avatar is clicked.
Let me know how to fix it.
const OverlayForm = ({ baby }) => {
return <BabyProfile baby={baby} />;
}
return (
<React.Fragment>
<View style={styles.main}>
{Babies.map((baby: Baby, index) => (
<View>
<TouchableOpacity
style={styles.button}
onPress={() => {
toggleOverlay;
myValue = baby;
}}
key={index}
>
<AvatarData baby={baby} />
</TouchableOpacity>
</View>
))}
<Overlay isVisible={visible} onBackdropPress={toggleOverlay}>
{OverlayForm(myValue)}
</Overlay>
</View>
</React.Fragment>
);
}
I solved as this by Quentin Grisel's advice.
You should handle the overlay inside your Baby avatar component and simply change its state to display/hide the overlay.
export default function App() {
return <BabyAvatar overlay={false} />;
}
const BabyAvatar = () => {
const [isOverlayed, setIsOverlayed] = React.useState(false);
const displayOverlay = () => setIsOverlayed(!isOverlayed);
return (
<>
<Text>Baby overlay: {isOverlayed ? 'true':'false'}</Text>
<Button onPress={() => displayOverlay()} title="Change overlay" />
</>
)
}

How to pass props to FlatList with renderScene in react-native-tab-view?

I'm a newbie in React Native and trying to pass props to ListHeaderComponent in FlatList
Here's the code:
const FirstRoute = (props) => {
const _renderHeader = () => {
return(
<View>
{props.isFollowed &&
<TouchableOpacity onPress={props.onSubmit}>
<Text>You have followed this person</Text>
</TouchableOpacity> }
</View>
)
}
return(
<View style={[styles.scene, { backgroundColor: '#ff4081' }]}>
<FlatList
data={data}
keyExtractor={item => item.id}
renderItem={itemData => ( <Image source={itemData.item.id} style={{height: WIDTH/3, width: WIDTH/3}} />)}
ListHeaderComponent={_renderHeader}
/>
</View>
)
};
const SecondRoute = () => (
<View style={[styles.scene, { backgroundColor: '#673ab7' }]} />
);
const initialLayout = { width: Dimensions.get('window').width };
export default function Parent() {
const [index, setIndex] = React.useState(0);
const [routes] = useState([
{ key: 'first', title: 'First' },
{ key: 'second', title: 'Second' },
]);
const [_isFollowed, setIsFollowed] = useState(false);
const _onSubmit = () => {
...
setIsfollowed(true)
}
const renderScene = ({route}) => {
switch(route.key){
case 'first': return <FirstRoute {...props} onSubmit={_onSubmit} isFollowed={_isFollowed} />
case 'second': return <SecondRoute {...props} />
}
};
return (
<TabView
navigationState={{ index, routes }}
renderScene={renderScene}
onIndexChange={setIndex}
initialLayout={initialLayout}
/>
);
}
But when I save it, the screen logs the error: Can't find the value of isFollowed
I think the problem is at the way I pass the props. I'm still learning it. Since when I delete the ListHeaderComponent, the FlatList still generates the list of images well. And I don't know if it has something to do with renderScene
I really don't understand why
Please help me. Thank you very much
Let me get this straight. You need to render _renderHeader dinamically based on _isFollowed state. So, you passed to the first route as props your _onSubmit function and _isFollowed state in order to get to access them at _renderHeader. Right?
As I see you actually doesn't need to do it once your _renderHeader has direct access to both _isFollowed state and _onSubmit function. Try it out as bellow:
export default function Parent() {
const [index, setIndex] = React.useState(0);
const [routes] = useState([
{ key: 'first', title: 'First' },
{ key: 'second', title: 'Second' },
]);
const [_isFollowed, setIsFollowed] = useState(false);
const initialLayout = { width: Dimensions.get('window').width };
function _onSubmit() {
setIsFollowed(true);
}
function _renderHeader() {
return (
<View>
{_isFollowed &&
<TouchableOpacity onPress={_onSubmit}>
<Text>You have followed this person</Text>
</TouchableOpacity> }
</View>
);
}
const FirstRoute = () => {
return(
<View style={[styles.scene, { backgroundColor: '#ff4081' }]}>
<FlatList
data={data}
keyExtractor={item => item.id}
renderItem={itemData => ( <Image source={itemData.item.id} style={{height: WIDTH/3, width: WIDTH/3}} />)}
ListHeaderComponent={_renderHeader}
/>
</View>
)
};
const SecondRoute = () => (
<View style={[styles.scene, { backgroundColor: '#673ab7' }]} />
);
const renderScene = ({route}) => {
switch(route.key){
case 'first': return <FirstRoute />
case 'second': return <SecondRoute />
}
};
return (
<TabView
navigationState={{ index, routes }}
renderScene={renderScene}
onIndexChange={setIndex}
initialLayout={initialLayout}
/>
);
}
Other point I don't understand in your code and couldn't check cause I didn't try to run it was the function bellow:
function _renderHeader() {
return (
<View>
{_isFollowed &&
<TouchableOpacity onPress={_onSubmit}>
<Text>You have followed this person</Text>
</TouchableOpacity> }
</View>
);
}
If you want to render TouchableOpacity just in case _isFollowed is true you should do it using ternary operator like so:
function _renderHeader() {
return (
<View>
{_isFollowed ?
<TouchableOpacity onPress={_onSubmit}>
<Text>You have followed this person</Text>
</TouchableOpacity> }
: <></>
}
</View>
);
}

How do I navigate between List and Item?

I am trying to open an item from my list but my item code is in another js. When I try to use onPress method there is no action. Also I am using Swipeout.
Here is my JobList.js where I am rendering the list of my items.
class JobList extends Component {
onJobDetails = (job) => {
this.props.navigate('JobDetails', job);
}
render() {
const { navigation } = this.props;
var renderJobs = () => {
return this.props.jobs.map((job) => {
return (
<JobItem
key={job._id}
title={job.title}
shortDescription={job.shortDescription}
logo={job.avatar}
company={job.company}
id={job._id}
dispatch={this.props.dispatch}
onPress={() => this.onJobDetails(job)}
/>
)
})
}
return (
<View style={styles.container}>
<ScrollView>
{renderJobs()}
</ScrollView>
</View>
);
}
};
And here is my JobItem.js
class JobItem extends Component {
render() {
return (
<Swipeout {...swipeSettings}>
<View style={styles.jobContainer}>
<View>
<Text style={styles.postTitle}>{this.props.title}</Text>
<Text style={styles.postShortDescription}>{this.props.shortDescription}</Text>
</View>
<View>
<Image
style={styles.postLogo}
source={{uri: '' + this.props.logo + ''}}/>
</View>
</View>
</Swipeout>
)
}
};
Any idea how shall I fix this?
You need to pass onPress prop to the child component in order for it to work.
<Swipeout {...swipeSettings}>
<TouchableWithoutFeedback onPress={this.props.onPress}>
//... other children here
</TouchableWithoutFeedback>
</Swipeout>

React Native loop this

when I put onPress in a map loop, it doesn't work. how to fix it?
var PageOne = React.createClass({
_handlePress() {
this.props.navigator.push({id: 2,});
},
render () {
return (
<View>
<TouchableOpacity onPress={this._handlePress}> //work here
<Text> One </Text>
</TouchableOpacity>
<View style={styles.albums}>
{
list.map(function(item, index){
return (
<TouchableOpacity key={index} onPress={this._handlePress}> //doesn't work hehre
<Text>{item}</Text>
</TouchableOpacity>
)
})
}
</View>
</View>
);
}
});
this is referring to the wrong context, you need the scope to be lexically bound, which is what the fat arrow function will do for you.
Try calling your function like this:
onPress={ () => this._handlePress() }
Also, you need to bind the map function to the correct context like this:
<View style={styles.albums}>
{
list.map(function(item, index){
return (
<TouchableOpacity key={index} onPress={() => this._handlePress()}>
<Text>{item}</Text>
</TouchableOpacity>
)
}.bind(this))
}
</View>
Or like this:
<View style={styles.albums}>
{
list.map((item, index) => {
return (
<TouchableOpacity key={index} onPress={() => this._handlePress()}>
<Text>{item}</Text>
</TouchableOpacity>
)
})
}
</View>
I set up a working project here.
https://rnplay.org/apps/_PmG6Q

Categories

Resources