How to update MapboxGL.ShapeSource dynamically? - reactjs

Using react-native-mapbox-gl/maps, when a SymbolLayer is dynamically added to a ShapeSource, it seems it is not shown, or the ShapeSource is not updated.
Here is the example to reproduce : based on CustomIcon example, I replaced the code with the code below. To reproduce, just execute the examples, copy-paste the code in place of the existing code in CustomIcon.js example.
import React from 'react';
import { View, Text } from 'react-native';
import MapboxGL from '#react-native-mapbox-gl/maps';
import sheet from '../styles/sheet';
import BaseExamplePropTypes from './common/BaseExamplePropTypes';
import Page from './common/Page';
import Bubble from './common/Bubble';
const styles = {
icon: {
iconAllowOverlap: true,
},
view: {
width: 60,
height: 60,
borderColor: 'black',
borderWidth: 1,
alignItems: 'center',
justifyContent: 'center'
},
text: {
fontSize: 50
}
};
const customIcons = ['😀', '🤣', '😋', '😢', '😬']
class CustomIcon extends React.Component {
constructor(props) {
super(props);
this.state = {
featureCollection: {
type: 'FeatureCollection',
features: [{
type: 'Feature',
geometry: {
coordinates: [-73.970895, 40.723279],
type: 'Point'
},
id: 1,
properties: {
customIcon: customIcons[0]
}
}]
},
};
this.onPress = this.onPress.bind(this);
this.onSourceLayerPress = this.onSourceLayerPress.bind(this);
}
onPress(e) {
const feature = {
type: 'Feature',
geometry: e.geometry,
id: Date.now(),
properties: {
customIcon: customIcons[this.state.featureCollection.features.length]
}
};
this.setState(({ featureCollection }) => ({
featureCollection: {
type: 'FeatureCollection',
features: [
...featureCollection.features,
feature
]
}
}));
}
onSourceLayerPress(e) {
const feature = e.nativeEvent.payload;
console.log('You pressed a layer here is your feature', feature); // eslint-disable-line
}
render() {
return (
<Page {...this.props}>
<MapboxGL.MapView
ref={c => (this._map = c)}
onPress={this.onPress}
style={sheet.matchParent}
>
<MapboxGL.Camera
zoomLevel={9}
centerCoordinate={[-73.970895, 40.723279]}
/>
<MapboxGL.ShapeSource
id="symbolLocationSource"
hitbox={{width: 20, height: 20}}
onPress={this.onSourceLayerPress}
shape={this.state.featureCollection}
>
{this.state.featureCollection.features.map((feature, ind) => (
<MapboxGL.SymbolLayer
id={"symbolLocationSymbols" + feature.id}
key={feature.id}
filter={['==', 'customIcon', customIcons[ind]]}
minZoomLevel={1}
style={styles.icon}
>
<View style={styles.view}>
<Text style={styles.text}>
{feature.properties.customIcon}
</Text>
</View>
</MapboxGL.SymbolLayer>
))}
</MapboxGL.ShapeSource>
</MapboxGL.MapView>
<Bubble>
<Text>Tap to add an icon</Text>
</Bubble>
</Page>
);
}
}
export default CustomIcon;
We can see that clicking on the map changes the state, adds a feature, but does not show the feature on the map.
How can we make the ShapeSource update dynamically ?

The whole discussion about the subject is in here: https://github.com/react-native-mapbox-gl/maps/issues/248
To make it short : I wanted to use dynamics SVGs as SymbolLayer (so that I can change the colour for instance), but this is not possible : giving SymbolLayer any child component is not a proper way to do.
We need instead to use Images in parallel of ShapeSource and SymbolLayer, because Images can be updated dynamically.
Here is a code example :
import React from 'react';
import MapboxGL from '#react-native-mapbox-gl/maps';
const myImages = {
'image-1': 'path/to/image-1',
'image-2': 'path/to/image-2'
}
const createFeature = ({
showPin,
icon = 'image-1', // as long as any added feature has an icon belonging to the static myImages, it works.
coordinates,
id
}) => ({
// https://github.com/react-native-mapbox-gl/maps/blob/master/docs/ShapeSource.md -> shapeSource prop
// https://geojson.org
// this has a geoJSON shape
type: 'Feature',
id,
properties: {
showPin,
icon
},
geometry: {
type: 'Point',
coordinates,
}
})
class MyMarkers extends React.Component {
state = {
featureCollection: MapboxGL.geoUtils.makeFeatureCollection(),
}
componentDidMount() {
this.updateFeatures()
}
componentDidUpdate(prevProps) {
// update features based on any criteria
if (conditionOnProps(prevProps, this.props)) this.updateFeatures()
}
updateFeatures() {
const featureCollection = MapboxGL.geoUtils.makeFeatureCollection()
for (let feature of this.props.features) {
MapboxGL.geoUtils.addToFeatureCollection(
featureCollection,
createFeature(feature)
)
}
this.setState({ featureCollection });
}
onPress = (e) => {
const feature = e.nativeEvent.payload;
this.props.doAnythingWithPressedFeature(feature);
}
render() {
return (
<>
<MapboxGL.Images images={myImages} />
<MapboxGL.ShapeSource
id='markersShape'
shape={this.props.featureCollection}
onPress={this.onPress}
>
<MapboxGL.SymbolLayer
id='markersSymbol'
filter={['==', 'showPin', true]}
style={{
iconAllowOverlap: true,
iconImage: ['get', 'icon'],
}}
/>
</MapboxGL.ShapeSource>
</>
)
}
}
export default MyMarkers;

Related

How can I display an array of images after get the urls React Native

Im trying to display a preview of the picked images after pick them, im using this library import { AssetsSelector } from 'expo-images-picker';
This is the code to pick the image:
import React, { useMemo } from 'react';
import { Text, View, StyleSheet, SafeAreaView, Alert } from 'react-native';
import { AssetsSelector } from 'expo-images-picker';
import { Ionicons } from '#expo/vector-icons';
import { AntDesign } from '#expo/vector-icons';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import { MediaType } from 'expo-media-library';
import { useNavigation } from '#react-navigation/core';
export default function App() {
const navigation = useNavigation();
const onSuccess = (data: any) => {
const filteredUri = data.filter(({ uri }) => uri).map(({ uri }) => uri);
navigation.navigate('AddProductScreen',
{
filteredUri: filteredUri,
});
};
const widgetErrors = useMemo(
() => ({
errorTextColor: 'black',
errorMessages: {
hasErrorWithPermissions: 'Please Allow media gallery permissions.',
hasErrorWithLoading: 'There was error while loading images.',
hasErrorWithResizing: 'There was error while loading images.',
hasNoAssets: 'No images found.',
},
}),
[]
);
const widgetSettings = useMemo(
() => ({
getImageMetaData: false,
initialLoad: 100,
assetsType: [MediaType.photo, MediaType.video],
minSelection: 1,
maxSelection: 3,
portraitCols: 4,
landscapeCols: 4,
}),
[]
);
const widgetResize = useMemo(
() => ({
width: 50,
compress: 0.7,
base64: false,
saveTo: 'jpeg',
}),
[]
);
const _textStyle = {
color: 'white',
};
const _buttonStyle = {
backgroundColor: 'orange',
borderRadius: 5,
};
const widgetNavigator = useMemo(
() => ({
Texts: {
finish: 'finish',
back: 'back',
selected: 'selected',
},
midTextColor: 'black',
minSelection: 1,
buttonTextStyle: _textStyle,
buttonStyle: _buttonStyle,
onBack: () => {navigation.goBack()},
onSuccess: (e: any) => onSuccess(e),
}),
[]
);
const widgetStyles = useMemo(
() => ({
margin: 2,
bgColor: 'white',
spinnerColor: 'blue',
widgetWidth: 99,
videoIcon: {
Component: Ionicons,
iconName: 'ios-videocam',
color: 'tomato',
size: 20,
},
selectedIcon: {
Component: Ionicons,
iconName: 'ios-checkmark-circle-outline',
color: 'white',
bg: '#0eb14970',
size: 26,
},
}),
[]
);
return (
<SafeAreaProvider>
<SafeAreaView style={styles.container}>
<View style={styles.container}>
<AssetsSelector
Settings={widgetSettings}
Errors={widgetErrors}
Styles={widgetStyles}
Navigator={widgetNavigator}
/>
</View>
</SafeAreaView>
</SafeAreaProvider>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
});
This is the code where I want o display the images, im using react navigation props to get the array:
const showPickedImages = ({ route, navigations }) => {
const navigation = useNavigation();
var filteredUri = route.params?.filteredUri;
return(
<View>
//Here I want to show the preview of the picked images
<View/>
)}
You can use Flatlist or ScrollView for this.
<Flatlist
ListEmptyComponent={
<Text>Loading...</Text>} // show loading text until you get the data
data={filteredUri}
renderItem={(uri)=>
<Image source={{uri}} style={{widht:100, height:100}} />
}
/>

FlatList renderItem not being highlighted when clicked the first time

Basically, I'm trying to setup a Flatlist in which multiple values can be selected.
My problem is with the styling of elements, when clicked the first time they don't get highlighted but when clicked the 2nd time they get highlighted.
FlatList Code
renderRow = ({item}) => (
<RowItem data={item} />
)
data = [
{
value: 'element1'
},
{
value: 'element2'
}
]
render(){
return (
<FlatList
data={this.data}
renderItem={this.renderRow}
keyExtractor={(item, index) => item + index}/>
)
}
RowItem Code
export default class RowItem extends React.Component {
state = {
isElementActive: false,
}
highlightElement = () => {
this.setState(prevState => ({
isElementActive: !prevState.isElementActive
}))
}
render() {
return (
<TouchableOpacity
activeOpacity={0.7}
onPress={this.highlightElement}
style={[styles.container, this.state.isElementActive ? styles.activeContainer : styles.inactiveContainer]}>
<Text>{this.props.data.value}</Text>
</TouchableOpacity>
)
}
}
const styles = Stylesheet.create({
container: {
height: 100,
width: 300,
backgroundColor: 'red',
},
activeContainer: {
opacity: 0.7,
},
inactiveContainer: {
opacity: 1,
}
});
When clicking on the element once, the value of the isElementActive returns true (when I console.log it) but the styling "activeContainer" does not apply. However, when I click it again, the styling applies even though the value of isElementActive then becomes false.
By default the value starts off as false, and they are not highlighted (i.e. have opacity of 1) and for this reason I'm kind of confused when clicked the first time, the value of isElementActive changes but the styling does not apply.
I was able to make it work with setOpacityTo after the setState.
Working example: https://snack.expo.io/SJNSKQPIB
import React from 'react';
import {TouchableOpacity, FlatList, StyleSheet, Text} from 'react-native';
type State = {
active: boolean;
};
type Props = {
value: string;
};
class RowItem extends React.Component<Props, State> {
state = {
active: null,
};
ref = null;
highlightElement = () => {
this.setState(
prevState => ({
active: !prevState.active,
}),
() => {
this.ref.setOpacityTo(this.state.active ? 0.7 : 1);
},
);
};
render() {
return (
<TouchableOpacity
ref={ref => (this.ref = ref)}
onPress={this.highlightElement}
style={[styles.container]}>
<Text>{this.props.value}</Text>
</TouchableOpacity>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
height: 100,
backgroundColor: 'red',
},
});
export default class App extends React.Component {
data = [
{
value: 'element1',
},
{
value: 'element2',
},
];
render() {
return (
<FlatList
keyExtractor={(_, index) => index.toString()}
data={this.data}
renderItem={({item}) => <RowItem value={item.value} />}
/>
);
}
}

React Native setState not causing rendering

I'm a complete beginner at react native and now I'm stuck with an update problem. I'm using react-native-paper and typescript.
In my app, I want to press a button and then the text field should change its text.
The problem is somehow at the button, or the called function because in the console log its always "before: true after:true" or "before: false after:false",
but what I expected is "before: true after: false" or vice-versa
I've also got a second Text View which is not shown at all.
Maybe someone can tell me what I am doing wrong?
My index.js
import * as React from 'react';
import { AppRegistry } from 'react-native';
import { Provider as PaperProvider } from 'react-native-paper';
import App from './src/App';
export default function Main() {
return (
<PaperProvider>
<App />
</PaperProvider>
);
}
AppRegistry.registerComponent('main', () => Main);
My MyNavigation.tsx (which contains currently my whole app).
import * as React from 'react';
import { BottomNavigation, Text, Avatar, Button, Card, Title, Paragraph, Banner } from 'react-native-paper';
import { View, Image, WebView } from 'react-native';
export default class MyNavi extends React.Component {
constructor(props, context) {
super(props, context);
this.setUnConnected = this.setUnConnected.bind(this);
}
state = {
index: 0,
routes: [
{ key: 'viewcamera', title: 'View', icon: 'remove-red-eye' },
{ key: 'viewsettings', title: 'Settings', icon: 'settings' },
{ key: 'viewhelp', title: 'How-To', icon: 'help' },
],
visible: true,
connected: false,
};
_handleIndexChange = index => { this.setState({ index }); }
setUnConnected = function () {
console.log("before: " + this.state.connected);
this.setState({ connected: !this.state.connected });
console.log("after: " + this.state.connected);
console.log("--------------");
};
ViewRoute = () =>
<View style={{ flex: 1, marginTop: 40 }}>
{/* --------- This text field does not get updated -------------*/}
<Text>connected: {this.state.connected ? 'true' : 'false'}</Text>
{/* --------- This text field is not shown at all ------------*/}
<Text>
{this.state.connected}
</Text>
<Button icon="camera" mode="contained" onPress={this.setUnConnected}>
Press me
</Button>
<View style={{ height: 400, width: 400 }}>
<WebView
source={{ uri: 'https://stackoverflow.com/' }}
style={{ marginTop: 40 }}
// onLoad={() => this.setState({ connected: true })}
/>
</View>
</View>
SettingsRoute = () => <Text>Settings</Text>;
HelpRoute = () => <View></View>
_renderScene = BottomNavigation.SceneMap({
viewcamera: this.ViewRoute,
viewsettings: this.SettingsRoute,
viewhelp: this.HelpRoute,
});
render() {
return (
<BottomNavigation
navigationState={this.state}
onIndexChange={this._handleIndexChange}
renderScene={this._renderScene}
/>
);
}
}
State Updates May Be Asynchronous React Documentation
So You cannot test your console.log in this way. Use the callback function of setState method as follows,
this.setState({ connected: !this.state.connected }, () => {
console.log("after: " + this.state.connected);
console.log("--------------");
});
Hope this will help you.
Your issue is here,
setUnConnected = function () {
console.log("before: " + this.state.connected);
this.setState({ connected: !this.state.connected });
console.log("after: " + this.state.connected);
console.log("--------------");
};
setState is async function and it takes some time to update the state. It does not block execution of next statements. So you will always get the previous state only for both the console.log.
To get the actual updated value, you should use callback in setState.
setUnConnected = function () {
console.log("before: " + this.state.connected);
this.setState({ connected: !this.state.connected }, () => console.log("after: " + this.state.connected); ); //Now you will get updated value.
console.log("--------------");
};
For this,
{/* --------- This text field is not shown at all ------------*/}
<Text>
{this.state.connected}
</Text>
this.state.connected is either true or false (Boolean) which will never be shown on screen. If you still want to see the value on screen, then you can use this hack.
<Text>
{this.state.connected.toString()}
</Text>
Update
From the docs,
Pages are lazily rendered, which means that a page will be rendered the first time you navigate to it. After initial render, all the pages stay rendered to preserve their state.
Instead of this,
_renderScene = BottomNavigation.SceneMap({
viewcamera: this.ViewRoute,
viewsettings: this.SettingsRoute,
viewhelp: this.HelpRoute,
});
You should use this version of renderScene,
_renderScene = ({ route, jumpTo }) => {
switch (route.key) {
case 'viewcamera':
return <ViewRoute jumpTo={jumpTo} connected={this.state.connected} setUnConnected={this.setUnConnected}/>; //Here you can pass data from state and function to your component
case 'viewsettings':
return <SettingsRoute jumpTo={jumpTo} />;
case 'viewhelp':
return <HelpRoute jumpTo={jumpTo} />;
}
}
Your complete code should look like this,
import * as React from 'react';
import { BottomNavigation, Text, Avatar, Button, Card, Title, Paragraph, Banner } from 'react-native-paper';
import { View, Image, WebView } from 'react-native';
const ViewRoute = (props) =>
<View style={{ flex: 1, marginTop: 40 }}>
{/* --------- This text field does not get updated -------------*/}
<Text>connected: {props.connected ? 'true' : 'false'}</Text>
{/* --------- This text field is not shown at all ------------*/}
<Text>
{props.connected.toString()}
</Text>
<Button icon="camera" mode="contained" onPress={props.setUnConnected}>
Press me
</Button>
<View style={{ height: 400, width: 400 }}>
<WebView
source={{ uri: 'https://stackoverflow.com/' }}
style={{ marginTop: 40 }}
// onLoad={() => this.setState({ connected: true })}
/>
</View>
</View>
const SettingsRoute = () => <Text>Settings</Text>;
const HelpRoute = () => <View></View>
export default class MyNavi extends React.Component {
constructor(props, context) {
super(props, context);
this.setUnConnected = this.setUnConnected.bind(this);
}
state = {
index: 0,
routes: [
{ key: 'viewcamera', title: 'View', icon: 'remove-red-eye' },
{ key: 'viewsettings', title: 'Settings', icon: 'settings' },
{ key: 'viewhelp', title: 'How-To', icon: 'help' },
],
visible: true,
connected: false,
};
_handleIndexChange = index => { this.setState({ index }); }
setUnConnected = function() {
console.log("before: " + this.state.connected);
this.setState({ connected: !this.state.connected });
console.log("after: " + this.state.connected);
console.log("--------------");
};
_renderScene = ({ route, jumpTo }) => {
switch (route.key) {
case 'viewcamera':
return <ViewRoute jumpTo={jumpTo} connected={this.state.connected} setUnConnected={this.setUnConnected}/>; //Here you can pass data from state and function to your component
case 'viewsettings':
return <SettingsRoute jumpTo={jumpTo} />;
case 'viewhelp':
return <HelpRoute jumpTo={jumpTo} />;
}
}
render() {
return (
<BottomNavigation
navigationState={this.state}
onIndexChange={this._handleIndexChange}
renderScene={this._renderScene}
/>
);
}
}

Mapping over data after fetching nested objects

Hello guys so i have component homescreen which i am fetching data inside and, the data i am getting contains some objects that has arrays inside, so i want to push all that arrays and data inside my state otherDetails key .
the data i am getting looks like this
{
id: 5,
url: "http://www.tvmaze.com/shows/5/true-detective",
name: "True Detective",
type: "Scripted",
language: "English",
genres: [
"Drama",
"Crime",
"Thriller"
],
status: "To Be Determined",
runtime: 60,
premiered: "2014-01-12",
officialSite: "http://www.hbo.com/true-detective",
schedule: {
time: "21:00",
days: [
"Sunday"
]
},
rating: {
average: 8.2
},
weight: 97,
network: {
id: 8,
name: "HBO",
country: {
name: "United States",
code: "US",
timezone: "America/New_York"
}
},
webChannel: null,
externals: {
tvrage: 31369,
thetvdb: 270633,
imdb: "tt2356777"
},
image: {
medium: "http://static.tvmaze.com/uploads/images/medium_portrait/178/445621.jpg",
original: "http://static.tvmaze.com/uploads/images/original_untouched/178/445621.jpg"
},
summary: "<p>Touch darkness and darkness touches you back. <b>True Detective</b> centers on troubled cops and the investigations that drive them to the edge. Each season features a new cast and a new case.</p>",
now what i am trying to do inside home screen i have my state with the object key otherDetails which i am trying to get genres language network schedule and summary so i am not sure what is happening wrong
this is my HomeScreen.js
import React, {Component} from 'react';
const axios = require('axios');
import Card from '../Components/Card/card';
import {
View,
Text,
Button,
Image,
ScrollView,
ActivityIndicator,
} from 'react-native';
import DetailsScreen from './detailsScreen';
import DetailedCard from '../Components/DetailedCard/DetailedCard';
export default class HomeScreen extends React.Component {
state = {
title: [],
image: [],
rating: [],
otherDetails:[{
genres:[],
schedule:[],
language:'',
network:[],
summary:'',
} ],
isLoading: true,
};
componentDidMount() {
this.getData();
}
getData = () => {
const requestUrls = Array.from({length: 9}).map(
(_, idx) => `http://api.tvmaze.com/shows/${idx + 1}`,
);
const handleResponse = data => {
const shows = data.map(show => show.data);
this.setState({
isLoading: false,
title: shows.map(show => show.name),
image: shows.map(show => show.image.medium),
rating: shows.map(show => show.rating.average),
otherDetails:shows.map((show,index)=>{
return [
show.genres[index],
show.schedule[index],
show.language[index],
show.network[index],
show.summary[index],
];
}),
});
};
const handleError = error => {
this.setState({
isLoading: false,
});
};
console.log(this.state.otherDetails.genres);
Promise.all(requestUrls.map(url => axios.get(url)))
.then(handleResponse)
.catch(handleError);
};
render() {
const {isLoading, title, image, rating, otherDetails} = this.state;
if (isLoading) {
return <ActivityIndicator size="large" color="#0000ff" />;
}console.log(this.state);
return (
<View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
<View style={{backGroundColor: 'red'}} />
<ScrollView style={{flex: 1}}>
<Card
title={this.state.title}
rating={this.state.rating}
source={this.state.image}
navigation = {this.props.navigation}
/>
</ScrollView>
<Text>here images</Text>
</View>
);
}
}
any help would be nice thank you in advanced ...!
Storing the response from tvmaze as an array instead of trying to map all the values to keys would make your life a bit easier.. Something like this should work:
import axios from 'axios'
import React from 'react'
import { ActivityIndicator, ScrollView, Text, View } from 'react-native'
import Card from '../Components/Card/card'
export default class HomeScreen extends React.Component {
state = {
shows: [],
isLoading: true
}
componentDidMount () {
this.getData()
}
getData = () => {
const requestUrls = Array.from({length: 9}).map(
(_, idx) => `http://api.tvmaze.com/shows/${idx + 1}`
)
const handleResponse = data => {
console.log(data)
this.setState({
isLoading: false,
shows: data
})
}
const handleError = error => {
console.log(error)
this.setState({
isLoading: false
})
}
Promise.all(requestUrls.map(url => axios.get(url)))
.then(handleResponse)
.catch(handleError)
}
render () {
const {isLoading, shows} = this.state
if (isLoading) {
return <ActivityIndicator size='large' color='#0000ff' />
}
console.log(this.state)
return (
<View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
<View style={{backGroundColor: 'red'}} />
<ScrollView style={{flex: 1}}>
{shows.length && shows.map(show => (
<Card key={show.data.id}
title={show.data.name}
{/*navigation={this.props.navigation}*/}
/>
))}
</ScrollView>
<Text>here images</Text>
</View>
)
}
}

I have a card component that I need to update to add new items

I built a card component that shows a list of user data and images with antd on nextJs. I want to build a functionality that creates a modal to input new data and image and adds it to the user interface as a new card, but I am confused on how to get my hands around it. I need assistance. Here's a link to my code!
import React from 'react';
import { Avatar, Card, Icon, List } from 'antd';
import { ICON_LIST, LIST_TEXTS, STYLES, USER_UPLOAD } from './constants';
const { AVATAR, CARD_CONTAINER, ICON, USER_LIST } = STYLES;
const { INNER, MORE, UPLOAD, VERTICAL } = LIST_TEXTS
class Home extends React.Component {
state = {
clicks: 0,
};
IncrementIconText = () => {
this.setState({ clicks: this.state.clicks + 1 });
}
render() {
const actions = ( ICON_LIST.map(({ type }) => (
<span>
<Icon key={type} type={type} onClick={this.IncrementIconText} style={ICON} />
{this.state.clicks}
</span>
)));
return (
<List
itemLayout={VERTICAL}
dataSource={USER_UPLOAD}
renderItem={item => (
<List.Item style={USER_LIST}>
<Card
actions={actions}
cover={<img alt={UPLOAD} src={item.image} />}
extra={<Icon type={MORE} />}
hoverable
title={<a><Avatar src={item.image} style={AVATAR} />{item.user}</a>}
type={INNER}
style={CARD_CONTAINER}
>
{item.story}
</Card>
</List.Item>
)}
/>
);
}
}
export default Home;
constants.js
export const ICON_LIST = [
{
key: "heart",
type: "heart",
},
{
key: "dislike",
type: "dislike",
},
{
key: "meh",
type: "meh",
},
]
export const LIST_TEXTS = {
INNER: "inner",
MORE: "more",
UPLOAD: "upload",
VERTICAL: "vertical",
};
export const STYLES = {
AVATAR: {
marginRight: 8
},
CARD_CONTAINER: {
width: "650px",
marginBottom: 50
},
ICON: {
marginRight: 8
},
USER_LIST: {
width: "100%",
display: "flex",
justifyContent: "center",
alignItems: "center"
},
};
export const USER_UPLOAD = [
{
image: "http://sugarweddings.com/files/styles/width-640/public/1.%20The%20Full%20Ankara%20Ball%20Wedding%20Gown%20#therealrhonkefella.PNG",
story: "Today's my birthday next week! What do you think?",
user: "Chioma",
},
{
image: "https://dailymedia.com.ng/wp-content/uploads/2018/10/7915550_img20181007141132_jpeg01c125e1588ffeee95a6f121c35cd378-1.jpg",
story: "Going for an event. Do you like my outfit",
user: "Simpcy",
},
{
image: "https://i0.wp.com/www.od9jastyles.com/wp-content/uploads/2018/01/ankara-styles-ankara-styles-gown-ankara-tops-ankara-gowns-ankara-styles-pictures-latest-ankara-style-2018-latest-ankara-styles-ankara-ankara-styles.png?fit=437%2C544&ssl=1",
story: "Saturdays are for weddings. Yay or nay!",
user: "Angela",
},
]
So this could get you started:
https://codesandbox.io/s/1r7j6lom34?fontsize=14
I moved your static USER_UPLOAD into the state of Home and wrote a method to add a new upload to that state.
You would now need to come up with a component that shows your modal and calls AddUpload with the right values.
Also your card-actions don't seem to function properly. To fix that i suggest creating a wrapper component for Card that has a state with the appropriate click counters. That way every card has its own clickcounters.

Resources