Creating a dynamic gallery in React Native, but images will not appear - json

I am trying to create a dynamic photo gallery for all the characters on my app. When I click on the gallery for that particular character, It takes me to their gallery, but no images appear. Each character should have 3 images for their gallery. While the app does not break, I do get a warning message saying the following: "Failed prop type: Invalid prop source supplied to image." Can somebody look at my code and see where I am wrong?
I created a JSON file with images for each character.
//CharacterImages.js
const CharacterImages = [
{
id: "1",
name: "Homer Simpson",
urlone:
"https://i.pinimg.com/474x/f1/36/ca/f136ca04817e60fa12f4a5680101ff8b.jpg",
urltwo:
"https://i.pinimg.com/474x/b1/da/e2/b1dae2fe6ca1620e5d1949a2dcd33a0c.jpg",
urlthree:
"https://i.pinimg.com/564x/7b/53/32/7b5332ef6a981b3c54e855495ea1c828.jpg"
},
{
id: "2",
name: "Marge Simpson",
urlone:
"https://i.pinimg.com/564x/63/e4/7d/63e47d98e66622bbff5e4578ccffeffc.jpg",
urltwo:
"https://i.pinimg.com/564x/04/48/60/044860ebcd5d6c14a1140b351cb620b1.jpg",
urlthree:
"https://i.pinimg.com/564x/6d/99/26/6d9926fa54bc3650acf9295d997fc72c.jpg"
},
{
id: "3",
name: "Bart Simpson",
urlone:
"https://i.pinimg.com/564x/fe/18/af/fe18af309234936e231fa107c6d2b4c7.jpg",
urltwo:
"https://i.pinimg.com/564x/20/59/7a/20597ab32ab0f7ec8a5484fa384e0bb4.jpg",
urlthree:
"https://i.pinimg.com/564x/20/59/7a/20597ab32ab0f7ec8a5484fa384e0bb4.jpg"
}
export default CharacterImages;
I created a separate folder so that the images can come in dynamically.
// ImageGallery.js
import React, { Component } from "react";
import { StyleSheet, Text, View } from "react-native";
import { SliderBox } from "react-native-image-slider-box";
import { withNavigation } from "react-navigation";
import CharacterImages from "../Data/CharacterImages";
class ImageGallery extends React.Component {
static navigationOptions = {
title: "Gallery",
headerStyle: {
backgroundColor: "#53b4e6"
},
headerTintColor: "#f6c945",
headerTitleStyle: "bold"
};
constructor(props) {
super(props);
this.state = {
CharacterImages
};
}
render() {
return (
<View style={styles.container}>
<SliderBox
images={this.state.CharacterImages}
sliderBoxHeight={900}
onCurrentImagePressed={index =>
console.warn(`image ${index} pressed`)
}
dotColor="yellow"
inactiveDotColor="white"
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1
}
});
export default withNavigation(ImageGallery);
Finally, the user can access the each characters gallery when they are in that particular profile this way:
//CharacterProfile.js
headerRight: (
<Button
onPress={() => navigation.navigate("ImageGallery")}
title="Gallery"
color="#f6c945"
/>
)

The problem here is that you are passing a whole object of datas instead of an array of strings with urls.
Try changing images prop to:
images = {this.state.CharacterImages.urlone}
Then i would advise to create a single "image" key object with the array of strings, making your CharacterImages be:
const CharacterImages = [
{
id: "1",
name: "Homer Simpson",
images:["https://i.pinimg.com/474x/f1/36/ca/f136ca04817e60fa12f4a5680101ff8b.jpg",
"https://i.pinimg.com/474x/b1/da/e2/b1dae2fe6ca1620e5d1949a2dcd33a0c.jpg",
"https://i.pinimg.com/564x/7b/53/32/7b5332ef6a981b3c54e855495ea1c828.jpg"]
},
{
id: "2",
name: "Marge Simpson",
images:["https://i.pinimg.com/564x/63/e4/7d/63e47d98e66622bbff5e4578ccffeffc.jpg",
"https://i.pinimg.com/564x/04/48/60/044860ebcd5d6c14a1140b351cb620b1.jpg",
"https://i.pinimg.com/564x/6d/99/26/6d9926fa54bc3650acf9295d997fc72c.jpg"]
},
{
id: "3",
name: "Bart Simpson",
images:["https://i.pinimg.com/564x/fe/18/af/fe18af309234936e231fa107c6d2b4c7.jpg",
"https://i.pinimg.com/564x/20/59/7a/20597ab32ab0f7ec8a5484fa384e0bb4.jpg",
"https://i.pinimg.com/564x/20/59/7a/20597ab32ab0f7ec8a5484fa384e0bb4.jpg"]
}
]
export default CharacterImages;
EDIT
Forgot that CharacterImages is an Array of objects!!!
I updated also the constant as i didn't close the array
Here you have to decide how you want to render your images based of which condition. I'm going to show you how to put them so all images gets rendered:
constructor(props){
super(props)
mergedArray=CharacterImages.map(item=> item.images).concat.apply([], mergedArray)
this.state={
images=mergedArr
}
}
After this you can use this.state.images inside your SliderBox

Related

Mapping through an Array of Images created in a local JSON File and rendering it

I am learning RN and I am having trouble setting up a mapping method to go through an array of images of some players that I created in a local JSON file, and render them in their respective profile pages.
This is how I have set my json.
//PlayerImages.js
const PlayerImages = [
{
id: "1",
name: "Cristiano Ronaldo",
images:["https://www.thesun.co.uk/wp-content/uploads/2019/05/NINTCHDBPICT000485852530.jpg",
"https://e00-marca.uecdn.es/assets/multimedia/imagenes/2019/05/18/15582064666477.jpg",
"https://e00-marca.uecdn.es/assets/multimedia/imagenes/2019/05/18/15582064666477.jpg"]
},
{
id: "2",
name: "Lionel Messi",
images:["https://www.thesun.co.uk/wp-content/uploads/2019/05/NINTCHDBPICT000485852530.jpg",
"https://e00-marca.uecdn.es/assets/multimedia/imagenes/2019/05/18/15582064666477.jpg",
"https://e00-marca.uecdn.es/assets/multimedia/imagenes/2019/05/18/15582064666477.jpg"]
},
{
id: "3",
name: "Neymar",
images: mages:["https://www.thesun.co.uk/wp-content/uploads/2019/05/NINTCHDBPICT000485852530.jpg",
"https://e00-marca.uecdn.es/assets/multimedia/imagenes/2019/05/18/15582064666477.jpg",
"https://e00-marca.uecdn.es/assets/multimedia/imagenes/2019/05/18/15582064666477.jpg"]
},
{
id: "4",
name: "Gabriel de Jesus",
images:["https://i.pinimg.com/474x/f1/36/ca/f136ca04817e60fa12f4a5680101ff8b.jpg",
"https://i.pinimg.com/474x/b1/da/e2/b1dae2fe6ca1620e5d1949a2dcd33a0c.jpg",
"https://i.pinimg.com/564x/7b/53/32/7b5332ef6a981b3c54e855495ea1c828.jpg"]
},
{
id: "5",
name: "Roberto Firmino",
images:mages:["https://www.thesun.co.uk/wp-content/uploads/2019/05/NINTCHDBPICT000485852530.jpg",
"https://e00-marca.uecdn.es/assets/multimedia/imagenes/2019/05/18/15582064666477.jpg",
"https://e00-marca.uecdn.es/assets/multimedia/imagenes/2019/05/18/15582064666477.jpg"]
}
]
export default PlayerImages;
This is how I have the ImageGallery component set up. Unfortunately, the way I have componentDidMount set up, shows Cristiano Ronaldos images in all profiles. How can I map it in order to make sure that each profile has the pictures of that particular player when you tap the gallery button on their profile?
//ImageGallery.js
import React, { Component } from "react";
import { StyleSheet, Text, View } from "react-native";
import { SliderBox } from "react-native-image-slider-box";
import { withNavigation } from "react-navigation";
import PlayerImages from "../Data/PlayerImages";
class ImageGallery extends React.Component {
static navigationOptions = {
title: "Player Gallery",
headerStyle: {
backgroundColor: "#53b4e6"
},
headerTintColor: "#f6c945",
headerTitleStyle: "bold"
};
constructor(props) {
super(props);
this.state = {
images: []
};
}
componentDidMount() {
let images = PlayerImages[0].images;
this.setState({ images });
}
render() {
return (
<View style={styles.container}>
<SliderBox
images={this.state.images}
sliderBoxHeight={900}
onCurrentImagePressed={index =>
console.warn(`image ${index} pressed`)
}
dotColor="yellow"
inactiveDotColor="white"
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1
}
});
export default withNavigation(ImageGallery);
Finally, when you are on the players profile, you should have access to their gallery by tapping the button in the header. As I mentioned earlier in the post, when you tap it, you get only the images of Cristiano Ronaldo.
//PlayerProfile.js
headerRight: (
<Button
onPress={() => navigation.navigate("ImageGallery")}
title="Gallery"
color="#f6c945"
/>
)
Pass the id of the player or the index of the player you want to display images of using navigation parameters , then use it to get the images of the player, right now you are using images at the 0th index of array.
componentDidMount() {
let images = PlayerImages[0].images;
this.setState({ images });
}
change this to
componentDidMount() {
let images = PlayerImages[index].images; //index is the index of the player whose images you want to show
this.setState({ images });
}

How to make aspecific API call from within a Line Chart class in ReactJS using react-chartjs-2?

I am creating a web app in ReactJS and I am trying to call an API from within my Chart class.
I have a program that takes data from a ML model and writes it to an API in the form of an array. I'm new to using ReactJS and I just want to make a call to the API to return the array into my data variable in react to render in the graph onscreen.
The data in the API is formatted as ..
[
1,
2,
3,
4
]
Currently I have the data hard coded into a separate file and am importing that but I want it to be called from the API directly so it updates.
import React, {Component} from "react"
import {Line} from "react-chartjs-2"
import {popuData, dayLabels} from "./FakeGraphData";
class PlanGraph extends Component{
constructor(props){
super(props);
this.state = {
chartData:{
labels: dayLabels,
datasets: [
{
label:'Predicted Park Crowds',
data: popuData,
borderColor: 'rgba(77, 112, 255, 1)',
backgroundColor: 'rgba(77, 112, 255, 1)'
}
]
}
}
}
render(){
return(
<div className = "chart">
<Line
data={this.state.chartData}
options={{
title: {
display:true,
text:"Predicted Park Crowds",
fontSize:25
},
legend:{
display: true,
position: 'right'
},
scales: {
yAxes: [{
scaleLabel: {
display: true,
labelString: 'Anticipated Crowds'
},
ticks: {
beginAtZero: true
}
}],
xAxes: [{
scaleLabel: {
display:true,
labelString: 'Days in the future'
}
}]
}
}}
/>
</div>
)
}
}
export default PlanGraph
Add a container Component & use Props
The component you've shown us here looks like a presentational component (has html structure, cares about how things look). What you should do is create a container component, these components care about things like logic & getting data. You can read about this design methodology here.
The container will render the component you have posted above but will pass some props kind of like this.
Example
class PlanGraphContainer extends Component {
state = {
dataToPass: []
};
async componentDidMount() {
const response = await fetch('https://your-api-request');
const data = await response.json(); // maybe you need this, you have to check your response
this.setState({dataToPass: data});
}
render() {
return <PlanGraph data={this.state.dataToPass} />
}
}
Then inside your PlanGraph use this.props.data to see the data that is being passed. make sure you have some fake data or loading state whilst you wait for the request to be complete. our you can add something like this
render() {
const { dataToPass } = this.state;
return (dataToPass && dataToPass.length)
? <PlanGraph data={this.state.dataToPass} />
: null;
}

Gatsby image path from json

Edit: Got it working even if it feels kinda wrong this way.. Any ideas to make it possible to only use the filename and not a relative path would be appreciated. Anyway, here is how I got it working:
data.json
[
{
"slug": "bloom",
"name": "Bloom",
"image": {
"src": "../images/socks1.jpg"
}
}
]
Query:
{
allDataJson {
edges {
node {
slug
name
image {
src {
childImageSharp {
fluid {
src
}
}
}
}
}
}
}
}
I am trying to display an image of which the name is stored in data.json and the file itself is in src/images
src/data/data.json
[
{
"slug": "sock1",
"name": "sock1",
"image": "socks1.jpg"
}
]
If instead of the pure filename, I provide a url to any image on the web it works.. Tried getting it to work as follows:
gatsby-config.js
module.exports = {
plugins: [
{
resolve: `gatsby-source-filesystem`,
options: {
name: `data`,
path: `${__dirname}/src/data`,
},
},
{
resolve: `gatsby-source-filesystem`,
options: {
name: `images`,
path: `${__dirname}/src/images`,
},
},
`gatsby-transformer-json`,
`gatsby-transformer-sharp`,
`gatsby-plugin-sharp`,
],
}
product.js
import React from 'react'
import { graphql } from 'gatsby'
import Layout from '../components/layout'
import get from 'lodash/get'
class ProductPage extends React.Component {
render() {
const products = get(this, 'props.data.allDataJson.edges')
console.log(products)
return (
<Layout>
{products.map(({ node }) => {
return (
<div key={node.name}>
<p>{node.name}</p>
<img src={node.image} />
</div>
)
})}
</Layout>
)
}
}
export default ProductPage
export const productsQuery = graphql`
query {
allDataJson {
edges {
node {
slug
name
image
}
}
}
}
`
All that's being outputted on the frontend is
<img src="socks1.jpg">
If I query for all files in the GraphiQl I get them, but (not sure about this one) I think the image field is just not recognized as file type field (cant query for src etc)
Any pointers would be much appreciated! :)
Your query should similar to this:
export const productsQuery = graphql`
query {
allDataJson {
edges {
node {
slug
name
image{
publicURL
childImageSharp{
fluid {
...GatsbyImageSharpFluid
}
}
}
}
}
}
}
`
Then you can output the path like this:
<img src={node.image.childImageSharp.fluid.src} />
You don't have to use fluid, you use other types of responsive images

displaying JSON data in flatlist

I want to display Json Data in a Flatlis component in my react-native App, but i can't figure out how to display it. I want to show the name of the dinosaur in a List and if i press on the list item i want to show the dimensions of the dinosaur.
I have the List items and the Detail screen all set up, but i get a blank screen with this code, did I import the JSON data in a wrong way or do i somehow have to restructure the data to display it?
I have JSON data like this:
{
"lambeosaurus": {
"dimensions": {
"height": 2.1,
"length": 12.5,
"weight": 5000
}
},
"stegosaurus": {
"dimensions": {
"height": 4,
"length": 9,
"weight": 2500
}
}
}
and this is my Code:
import React from 'react'
import { StyleSheet, Text, View, TouchableOpacity, StatusBar, FlatList, Image } from 'react-native'
import MyListItem from '../components/MyListItem'
const data = require("../data/MockData.json")
class HomeScreen extends React.Component {
render() {
return (
<View>
<StatusBar hidden={true} />
<FlatList
data={data}
renderItem={({ item }) =>
<MyListItem
item={item}
onPress={() => {
this.props.navigation.navigate('Details', {
item: item
})
}}
/>
}
/>
</View>
);
}
}
export default HomeScreen;
FlatList expects an array as data prop, but your JSON is an object. You could change it into an array before using it.
// const objData = require("../data/MockData.json");
const objData = {
lambeosaurus: {
dimensions: {
height: 2.1,
length: 12.5,
weight: 5000
}
},
stegosaurus: {
dimensions: {
height: 4,
length: 9,
weight: 2500
}
}
};
const data = Object.keys(objData).map(key => ({
key,
...objData[key]
}));
console.log(data);

How to let one component correspond with the other for a specific function

I've got a component where I click a color of a machine, when I change colors, the machine gets loaded with a different color inside a image carousel.
Now I also created a component in the bottom with a image gallery of the same machine. How can I make it that the image gallery also changes color when I click the color button in the top of the page?
Important notice: The two components are not in the same parent component but they do load in the same machine images already, so the methods are not wrong I believe.
this is the clickable color button:
<li
v-for="(color, index) in machine.content[0].machine_colors"
:key="color.color_slug"
v-if="color.inStock"
v-on:click="selectColor(index)"
v-bind:class="{ active: (color.color_slug === selectedColor.color_slug)}">
<img v-bind:src="color.color_dash">
</li>
this is the component that changes color:
<div class="product__carousel">
<Carousel showIcon v-if="selectedColor" :machineColor="selectedColor"/> <!-- Image carousel gets loaded in -->
</div>
and the component that needs to change color but does not:
<div id="tab-two-panel" class="panel">
<footerGallery v-if="selectedColor && machine" :machineColor="selectedColor"/>
</div>
Heres the script of the partent component:
export default {
name: 'aboutMachine',
components: {
Collapse,
footerGallery,
},
data() {
return{
selectedColor: this.getMachineColorContent(),
}
},
props: {
main: {
default () {
return {};
},
},
machine: {
default () {
return {};
},
},
},
methods: {
getMachineColorContent() {
if (this.selectedColor) {
return null;
}
return this.machine.content[0].machine_colors[0];
},
selectColor(index) {
this.selectedColor = this.machine.content[0].machine_colors[index];
},
},
}
and the component itself:
export default {
name: 'footerGallery',
props: {
showIcon: Boolean,
machineColor: {
default () {
return {};
},
},
},
data() {
return {
highLightedThumbIndex: 0,
isActive: undefined,
};
},
created() {
this.highLightedThumbIndex = this.highLightedThumbIndex || 0;
},
methods: {
selectThumb(index) {
this.highLightedThumbIndex = index;
},
},
};
This is my main.js
import Vue from 'vue';
import VueYouTubeEmbed from 'vue-youtube-embed'
import FontAwesome from './libs/fa';
import App from './App';
const eventHub = new Vue();
Vue.use(VueYouTubeEmbed);
Vue.component('font-awesome-icon', FontAwesome);
Vue.config.productionTip = false;
/* eslint-disable no-new */
new Vue({
el: '#app',
components: { App },
template: '<App/>',
});
I would use events to accomplish this. The migration guide to Vue2 has a good short explanation of how to do simple event routing without using a full Vuex solution. In your case, you would declare a global event hub in one of your js files:
var eventHub = new Vue();
In your selectColor method you would emit the index selected:
selectColor(index) {
this.selectedColor = this.machine.content[0].machine_colors[index];
eventHub.$emit("select-color",index);
}
And in the footer, you would register a listener for the select-color event that calls selectThumb with the payload of the event (which is the selected index):
created() {
this.highLightedThumbIndex = this.highLightedThumbIndex || 0;
eventHub.$on("select-color",this.selectThumb);
}