Finding the specific child of a component in react-native - javascript

I am creating Badges Or Chips like these
Using the code:
<View style = {{flexDirection: 'row', marginLeft: 10, marginTop: 5}}>
{this.state.eduModes.map((v, i) => {
return (
<Badge
key={i}
onPress = {(i) => {
console.log(i);
}}
value={v}
containerStyle= {{marginRight: 5}}
textStyle={{ color: 'orange' }}
/>
)
})}
</View>
The user chooses the value from a picker which creates the badge, now what I want is when the user clicks on the badge the badge should be removed. So how can I access the specific badge on which the user clicked so it would disappear on re-rendering?

You could create a new inline function that sends the index of the badge that should be removed to the remove function.
Example
class App extends React.Component {
handlePress = index => {
this.setState(previousState => {
const eduModes = [...previousState.eduModes];
eduModes.splice(index, 1);
return { eduModes };
});
};
render() {
return (
<View style={{ flexDirection: "row", marginLeft: 10, marginTop: 5 }}>
{this.state.eduModes.map((v, i) => {
return (
<Badge
key={i}
onPress={() => this.handlePress(i)}
value={v}
containerStyle={{ marginRight: 5 }}
textStyle={{ color: "orange" }}
/>
);
})}
</View>
);
}
}

Related

How to render background for each section in SectionList component?

How to render the background for each section?
<SectionList
keyExtractor={(item, index) => index.toString()}
sections={list}
renderItem={({ item }) => <Text>{item.name}</Text>}
renderSectionHeader={({ section }) => {
return (
<View bg-bg6ColorLabel style={{ width: 170, borderRadius: 500, alignSelf: 'center', marginTop: 20 }}>
<Text textColor style={{ textAlign: 'center', padding: 7, fontWeight: 'bold', fontSize: 16 }}>{section.titleWithDay}</Text>
</View>
)
}}
/>
I want to have background under each item of section.
Just like this:
<SectionList
keyExtractor={(item, index) => index.toString()}
sections={list}
renderItem={props => {
if (props.index < 1) {
return (
<View>
<FlatList
data={props.section.data}
renderItem={({ item }) => (
<Text>{item.name}</Text>
)}
/>
</View>
);
} else {
return null;
}
}}
renderSectionHeader={({ section }) => {
return (
<View >
<Text>{section.titleWithDay}</Text>
</View>
)
}}
/>

React Native - How to set blurRadius onto a selected image

My app displays images and other info getting data from a JSON, and I want to set a blurRadius on the selected image when the unblur function is called.
My code is the following:
const [blur, setBlur] = useState(160);
const unblur = () => {
if(blur == 160){
setBlur(0);
}
}
{isLoading ? <ActivityIndicator/> : (
<FlatList
data={data}
keyExtractor={({ id }, index) => id}
renderItem={({ item }) => (
<>
<Text style={{ textAlign: "left", color: 'white', fontSize: 24 }}>
<Image style={{ alignSelf: "center" }} source={{uri: item.profile_picture, width: 48, height: 48}} />
{item.username}
</Text>
<TouchableOpacity onPress={() => unblur()}>
<Image style={{ alignSelf: "center" }} blurRadius={blur} source={{uri: item.picture, width: 400, height: 320}} />
</TouchableOpacity>
<Text style={{ textAlign: "left", color: 'white', fontSize: 24 }}>
ID {item.id}
</Text>
<Text>
{'\n'}{'\n'}
</Text>
</>
)}
/>
)}
I want to change the {blur} value for a given image when I tap the TouchableOpacity component of the image I want to unblur. Right in this moment when I tap an image, all the images in the list are getting unblurred.
You should add the blur values inside your items like this. Create a new components called Items. Then, add the blur state inside it. (not inside Main).
const Main = () => {
if (isLoading) {
return;
<ActivityIndicator />;
}
return (
<FlatList
data={data}
keyExtractor={({ id }, index) => id}
renderItem={({ item }) => <Item item={item} />}
/>
);
};
const Item = ({ item }) => {
const [blur, setBlur] = useState(160);
const unblur = () => {
if (blur == 160) {
setBlur(0);
}
};
return (
<>
<Text style={{ textAlign: "left", color: "white", fontSize: 24 }}>
<Image
style={{ alignSelf: "center" }}
source={{ uri: item.profile_picture, width: 48, height: 48 }}
/>
{item.username}
</Text>
<TouchableOpacity onPress={() => unblur()}>
<Image
style={{ alignSelf: "center" }}
blurRadius={blur}
source={{ uri: item.picture, width: 400, height: 320 }}
/>
</TouchableOpacity>
<Text style={{ textAlign: "left", color: "white", fontSize: 24 }}>
ID {item.id}
</Text>
<Text>
{"\n"}
{"\n"}
</Text>
</>
);
};
Also, may I suggest you can use 'onPressIn'. That way, image will unblur when the finger tap the image, not press the image. But that was your decision.

how do i get and show the total sum objects that arrive from my JSON in a footer at the bottom of the screen?

At my example, the function “getData” loading my data, but after the loading, I try to print and show the total sum of the objects that came from JSON in a footer at the bottom of the screen.
and I don't really know how to do it.
I don't understand how to solve this issue coz I have tried many ways.
This is my example:
export default class MainScreen extends Component {
constructor(props) {
super(props);
this.state = { data: [] };
}
getData = () => {
this.setState({ isLoading: true })
axios.get("https://rallycoding.herokuapp.com/api/music_albums")
.then(res => {
this.setState({
isLoading: false,
data: res.data
});
console.log(res.data);
});
}
componentDidMount() {
this.props.navigation.setParams({getData: this.getData}); //Here I set the function to parameter
this.getData()
}
renderItem(item) {
const { title, artist} = item.item;
return (
<TouchableOpacity
onPress={() => this.props.navigation.navigate("Settings")}
>
<Card
containerStyle={{
borderColor: "black",
padding: 20,
height: 100,
backgroundColor: "#e6e6ff",
borderBottomEndRadius: 10,
borderTopRightRadius: 10,
borderBottomStartRadius: 10,
}}
>
<View
style={{
paddingVertical: 15,
paddingHorizontal: 10,
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center"
}}
>
<Icon name="chevron-right" size={30} color={"grey"} justifyContent={"space-between"} />
<Text style={styles.name}>
{title+ " " + artist}
</Text>
{/* <Text style={styles.vertical} numberOfLines={2}></Text> */}
</View>
</Card>
</TouchableOpacity>
);
}
render() {
if (this.state.isLoading) {
return (
<View style={{ flex: 1, paddingTop: 230 }}>
<Text
style={{ alignSelf: "center", fontWeight: "bold", fontSize: 20 }}
>
loading data...
</Text>
<ActivityIndicator size={'large'} color={'#08cbfc'} />
</View>
);
}
return (
<View style={styles.container}>
<FlatList
data={this.state.data}
renderItem={this.renderItem.bind(this)}
keyExtractor={item => item.id}
/>
</View>
);
}
}
/////////////////////////////////////////////////////////
MainScreen.navigationOptions = navData => {
return {
headerTitle: 'melon',
headerRight: (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title=**"sync button"**
iconName={Platform.OS === "android" ? "md-sync" : "ios-sync"}
onPress={() => {
navData.navigation.navigate("getData");
}}
/>
</HeaderButtons>
)
};
};
If type of data is array you can get total number of elements by this.state.data.length
If type of data is object you can get total number of elements by Object.keys(data).length

Rendering Child Component in React Native

I am using React Native and React Navigation to build a simple app.
I have got the basic structure working with stub state but I am having problem with changing state via callback and re-render.
In my screen, I have simple start button
`<View style={styles.buttonContainer}>
<TouchableOpacity
style={[myStyles.buttonStyle, { backgroundColor: color }]}
onPress={() => handlePress(button.title)}
>
<Text style={myStyles.textStyle}>{button.title}</Text>
</TouchableOpacity>
</View>`
Problem:
After I update my parent Component state, my child component does not instantly render to match the state change. I understood React will re-render all child components when parent state is changed?
Instead, I have to move back to previous screen and navigate again to my button screen to see that the button's color and text has changed correctly.
I've read about requiring a componentWillReceiveProps(nextProps) handler but I am not sure how to use it. I put a console.log('nextProps', nextProps) inside but it does not get fired.
From navigation perspective, the Root component is on index[0] and my button view is at index[3] so it's the 3rd screen from the root.
EDIT 1: Added Code
myButton screen:
export class TeamsScreen extends React.Component {
static navigationOptions = ({ navigation }) => ({
title: `${navigation.state.params.game.name}: Select Team`,
headerTintColor: 'white',
headerStyle: {
backgroundColor: 'black',
},
headerVisible: true
})
componentWillReceiveProps(nextProps) {
console.log('nextProps', nextProps);
}
render() {
const { navigate, setParams } = this.props.navigation;
const { game, player, setGameState } = this.props.navigation.state.params;
const color = game.status === 'Start' ? 'green' : 'red';
const index = game.indexOf(player);
const status = game.status;
console.log('index', index);
console.log('status', status);
return (
<View style={styles.container}>
<View style={styles.buttonContainer}>
<TouchableOpacity
style={[myStyles.buttonStyle, { backgroundColor: color }]}
onPress={() => setGameState(index, status)}
>
<Text style={myStyles.textStyle}>{game.status}</Text>
</TouchableOpacity>
</View>
<View style={styles.buttonContainer}>
<Button
onPress={() => navigate('ChangeDriverScreen', { team, game })}
title='Change Driver'
/>
</View>
<View style={{ marginTop: 40, marginBottom: 20 }}>
<Text style={{ fontSize: 16, color: 'white', alignSelf: 'center' }}>Teams</Text>
</View>
<View style={{ height: 250 }}>
<FlatList
data={player.teams}
renderItem={({item}) =>
<View style={styles.buttonContainer}>
<Button
onPress={() => navigate('TeamSelectedStartScreen', { team: item })}
title={item.name}
/>
</View>}
keyExtractor={item => item.name}
/>
</View>
<Image
style={{ alignSelf: 'center', justifyContent: 'flex-end', height: 75, width: 250, resizeMode: 'stretch'}}
source={require('./images/icons/playerPlaceholder.png')}
/>
</View>
)}}
Then the onPress function that is called back:
setGameState = (gameIndex, status) => {
console.log('setGameState', gameIndex, status);
console.log('gameStateBefore', this.state.game);
const newGameState = this.state.game.map(t => {
console.log(this.state.game.indexOf(t));
if (this.state.game.indexOf(t) === gameIndex) {
const newStatus = status === 'Start' ? 'Stop' : 'Start';
t.status = newStatus; /*eslint no-param-reassign: "off"*/
console.log('inside if', t.status);
console.log('inside if game', t);
return t;
}
return t;
});
console.log('new Game State', newGameState);
this.setState(() => ({
game: newGameState
}));
}
So the setState method works (as re-navigating back to screen 3 shows the correct state but core question is how to get immediate re-render of screen 3 when setState is called from Screen 0.

How to create two columns with space beetwen in react native - flatList

Hi i'm new in React Native.
I am trying to create two columns layout with space beetween using react native component called flatList.
Here is my view Code:
<View style={styles.container}>
<FlatList
data={db}
keyExtractor={ (item, index) => item.id }
numColumns={2}
renderItem={
({item}) => (
<TouchableWithoutFeedback onPress ={() => showItemDetails(item.id)}>
<View style={styles.listItem}>
<Text style={styles.title}>{item.name}</Text>
<Image
style={styles.image}
source={{uri: item.image}}
/>
<Text style={styles.price}>{item.price} zł</Text>
</View>
</TouchableWithoutFeedback>
)
}
/>
</View>
Here is styles:
container: {
flex: 1,
flexDirection: 'row',
justifyContent: 'space-between',
padding: 10,
marginBottom: 40
},
listItem: {
maxWidth: Dimensions.get('window').width /2,
flex:0.5,
backgroundColor: '#fff',
marginBottom: 10,
borderRadius: 4,
},
And result is two columns but without space between.
Could you help me resolve this problem ?
You can give the item itself a width value of 45%. Also, flatlist has a property called columnWrapperStyle that you can give the value justifyContent: 'space-between.
Heres an example:
<FlatList
columnWrapperStyle={{justifyContent: 'space-between'}}
data={ApiData}
numColumns={2}
renderItem={({item}) => {
return (
<item style={{width: '45%'}} />
);
}}
/>
Use ItemSeparatorComponent for render a compontent between items
Docs: Rendered in between each item, but not at the top or bottom.
<FlatList
data={arrayOfData}
horizontal
ItemSeparatorComponent={
() => <View style={{ width: 16, backgroundColor: 'pink' }}/>
}
renderItem={({ item }) => (
<ItemView product={item} />
)}
/>
Preview in horizontal list
If list is vertical and suppose columnCount is 2
You have to give the styles.container to the contentContainerStyle propety of Flatlist, like so:
<FlatList
data={db}
keyExtractor={ (item, index) => item.id }
contentContainerStyle={styles.container}
numColumns={2}
renderItem={
({item}) => (
<TouchableWithoutFeedback onPress ={() => showItemDetails(item.id)}>
<View style={styles.listItem}>
<Text style={styles.title}>{item.name}</Text>
<Image
style={styles.image}
source={{uri: item.image}}
/>
<Text style={styles.price}>{item.price} zł</Text>
</View>
</TouchableWithoutFeedback>
)
}
/>
Just add some margin to the style of the list Item.
listItem: {
margin: 10,
}
How to create two columns with equal spacing between items:
<FlatList
data={DATA}
renderItem={renderItem}
keyExtractor={(item) => item.id}
horizontal={false} // you must include this line when using numColumns [per the documentation][1]
numColumns={2} // creates two columns
key={2} // add key to prevent error from being thrown
columnWrapperStyle={{justifyContent: 'space-between'}} // causes items to be equally spaced
>
Also, this will set each column to 1/2 the screen width:
const styles = StyleSheet.create({
item: {
flex: 1/2,
}})
I haven't used this library, but adding padding: 10 to listItem styles should help.
Based on your example it looks like you can add a margin to your list item styles:
listItem: {
maxWidth: Dimensions.get('window').width /2,
flex:0.5,
backgroundColor: '#fff',
marginBottom: 10,
borderRadius: 4,
margin: 18,
},
Keep in mind that this is equivalent to doing:
listItem: {
maxWidth: Dimensions.get('window').width /2,
flex:0.5,
backgroundColor: '#fff',
marginBottom: 10,
borderRadius: 4,
marginTop: 18,
marginBottom: 18,
marginRight: 18,
marginLeft: 18,
},
Customize to your liking or spec :)
Assuming your items are flex:1 and no width specified. You can wrap your renderItem with another view that adds the padding if needed
function columnWrappedRenderItemFunction<ItemT>(
renderItem: ListRenderItem<ItemT>,
numColumns: number,
space: FlatListProps<ItemT>["space"],
numberOfItems: number
): FlatListProps<ItemT>["renderItem"] {
return function columnWrappedRenderItem({
index,
...props
}: Parameters<ListRenderItem<ItemT>>[0]): ReturnType<ListRenderItem<ItemT>> {
const needsGapOnLeft = index % numColumns !== 0;
let extraItems = 0;
if (index === numberOfItems - 1) {
extraItems = (numColumns - (numberOfItems % numColumns)) % numColumns;
}
if (needsGapOnLeft) {
return (
<>
<View style={{width:space}} />
{renderItem({ index, ...props })}
{Array.from({ length: extraItems }, (_, k) => (
<View key={"extra_" + k} style={{marginLeft:space, flex:1}} />
))}
</>
);
} else {
return (
<>
{renderItem({ index, ...props })}
{Array.from({ length: extraItems }, (_, k) => (
<View key={"extra_" + k} marginLeft={space} style={{flex:1}} />
))}
</>
);
}
};
}
e.g.
function myRenderItem() { ... }
...
return (<FlatList
...
data={data}
renderItem={
numColumns === 1
? renderItem
: columnWrappedRenderItemFunction(
renderItem,
numColumns,
space,
data.length
)
}
numColumns={numColumns}
ItemSeparatorComponent={() => <View style={{height: space}} />}
/>);

Categories