I'm facing an issue when I try to display images inside my React App component
I have all the images I need, divided by set, in this path src/cards_images/ inside root folder.
In this way I can reach for cards in different sets like:
src/cards_images/set1_MyFirstSetName_MyCard1.jpg
src/cards_images/set2_MySecondSetName_MyCard1234.jpg
I need to create images dynamically, because on this component I pass an array of objects, with the right source path. If I hard-code the same source path, it works, but if I dynamically try to create the path, React says it can't find the module.
I can't put every image inside Public folder because we are talking about several thousands of 200kb images and it won't load. Neither I can import/require every image in bulk, because it will not work aswell (still, how to filter/show them?). It has to be dynamically required and showed. How can I do it? Am I doing anything wrong on this?
import React , {Component}from 'react';
function CardDisplay({ sets }) {
return (
<div id="myDeckCardPickerContainer">
{sets?.map(settino => {
// Gives error "Module not found"
var temp = require(settino.url)
return <div key={settino.key} className='cardImageContainer'><img className='cardImagePreview' src={temp} alt={settino.Name}></img><div><span className='addCardToDeck'>➕</span>{settino.name}<span className='removeCardFromDeck'>➖</span></div></div>
// Gives error "Module not found"
return <div key={settino.key} className='cardImageContainer'><img className='cardImagePreview' src={require(settino.url)} alt={settino.Name}></img><div><span className='addCardToDeck'>➕</span>{settino.name}<span className='removeCardFromDeck'>➖</span></div></div>
// Works fine
return <div key={settino.key} className='cardImageContainer'><img className='cardImagePreview' src={require('../cards_images/set1_MyFirstSetName_MyCard1.jpg')} alt={settino.Name}></img><div><span className='addCardToDeck'>➕</span>{settino.name}<span className='removeCardFromDeck'>➖</span></div></div>
})}
</div>
)}
export default CardDisplay;
Related
I see opportunities to minimize network bytes transfer of my website, but can't come up with a proper solution.
In Gatsby, I make use of .mdx files. In these files I can use React Components, such as:
<Cards id="id_1" />
There are dozens of cards defined in a .json file, which can be used across the website by just calling this component in de mdx file and passing their id.
The Cards component looks like somewhat like this:
import React from 'react'
import Img from 'gatsby-image';
import { StaticQuery, graphql } from 'gatsby';
const Cards = (props) => {
const card_id = props.id ? props.id.slice(0, 2) : [] // grab id
return <StaticQuery
query={graphql`
query Query {
images: allFile(filter: { sourceInstanceName: { eq: "card-images" } }) {
edges {
node {
childImageSharp {
fluid(maxHeight: 256, quality: 100) {
...GatsbyImageSharpFluid_withWebp
...GatsbyImageSharpFluidLimitPresentationSize
}
}
name
}
}
}
allCardsJson {
nodes {
name
id
}
}
}
`}
render={(data) => {
return(
// returns a component by filtering 'data' by 'card_id'
)
}}
/>
}
Everything works fine, but...
When this component is used, the full result of StaticQuery (meaning: all cards since filtering is done inside the return, not inside the staticquery) is send to visitors of the page. This is unnecessary and a waste of network bandwidth, because for example only one card (or a few) is used on the page.
I understand that a StaticQuery is .. static. Thus, I cannot dynamically filter within this query to limit the size of the result.
DynamicQuerys are used when building pages, not inside components.
Is it possible to somehow create components with dynamic content (defined elsewhere), but limited to just the required data? (like by providing an id like I've tried)
I am thinking about creating a seperate file for each Card. Then import the Cards needed into the mdx file and passing it to the component. Thoughts?
There is no documentation about my use case. This makes me wonder if I'm using it as intended.
I solved it by adding the used data to the page context by editing the gatsby-node.js:
Get the mdxAST of the post, filter the components, then filter the ids used.
Add this list of ids to the page context.
Move the query stated in the question above to the post GraphQL query. Use the list of ids provided by page context to filter the data down to only the used ids in this post.
Pass this data as a property to the MDXRenderer.
Pass this data in the .mdx file to the cards component, such as: <Cards data={props.cards_data} />.
Now the compontent received the data without using a StaticQuery.
This works, but it feels kinda weird. There must be a better, more clean, solution in my opinion.
GraphQL queries are run ONCE when you run gatsby develop or gatsby build. This is a counterintuitive way how Gatsby works but read it yourself:
Gatsby uses GraphQL at build-time and not for live sites.
More information about the gatsby build process and how GraphQL factors into it.
This means you already built your component the way you are supposed to. During build all your cards are queried and kept in memory. When creating the HTML for your pages with your cards, only the cards with your ID are used to build your page. Thus the user only sees gets transmitted the pages with filtered IDs.
You can double check if really only the cards with the ID are inside your page:
run gatsby clean: make sure old fragments of your page are removed
run gatsby build: Create your site from scratch
Check your public folder in your project root. This is the classic webpage that gatsby builds. Navigate to your pages with your cards for example /public/blog/example-page-with-card. Take a look inside the HTML of the page: Does it contain all the cards or just the one with the IDs you need?
I'm new to React and I'm trying to create a game involving cards where a websocket returns an array of card names, and on the front end I generate the correct card images from a local directory of images. The card names returned from the websocket match the file name of the image, e.g. "map" corresponds to the image "map.jpg". I'm attempting to map over the array & create an image tag for each card name in the array. I read that the best way is to use require() and so I attempted to do something like
//inside component
let cardImgs;
...
socket.on(RECEIVED_STARTING_HAND, (hand) => {
cardImgs = hand.map(cardName => <img src= {require(`../../card_images/${cardName}.jpg`)}/>
})
...So that later on in my component I could do something like this:
return (
<div>
<h1>My hand</h1>
{cardImgs}
</div>
...
)
However, cardImgs does not render. In my return statement of my component I tested out rendering an image directly and it worked, e.g.
const map = "map"
return (
<div>
<h1>My hand</h1>
{cardImgs}
</div>
...
//this one renders!!!
<img src={require(`../../card_images/${map}.jpg`)} />
)
I created my react app with Create-React-App and I read some things that said I should reconfigure my webpack.config.js file but then I also read some things that said I should not touch it unless I needed to. I am not sure why require() works when I do it in my return statement of my component yet it's not working when given the array of image tags. Am I missing something or do you have any suggestions on better ways to dynamically load images in React? Thank you!
I recommend simply processing the state item hand in your render. When you update the value, a render will be triggered and create your images.
{hand.map((cardName, index) => (
<img key={cardName} src={`pathtoserver/card_images/${cardName}.jpg`}/>
))}
Demo
if you want render a image from a local folder you can import images that you need with import syntax and after that use it in your img tag
import card1 from '../you/path/to/image.png';
import card2 from '../you/path/to/image.png';
import card3 from '../you/path/to/image.png';
const cards = {
nameToCard1: card1,
nameToCard2: card2,
nameToCard3: card3,
}
in your socket method:
socket.on(RECEIVED_STARTING_HAND, (hand) => {
cardImgs = hand.map(cardName => cards[cardName] />
})
and in your render method:
return (
<div>
<h1>My hand</h1>
<ul>
{cardImgs.map((img) => <li><img key={img} src={img} /></li>)}
</ul>
</div>
)
i hope this help you, have a nice day.
Imagine you made a web framework that helps you quickly make blogs for clients. For the sake of this post, its the same blog template everytime, what changes is the content. You're React app is a simple structure of the following [where the Content state is just changing each time]
<App>
<Navigation/>
<Content/>
</App>
What makes the framework is you have XML files which contain the HTML. Each XML file represents one blog post. The app pulls all the HTML from the XML files, and puts it into the state of the App in a "blog posts" array. Depending on the state of the app, a specific entry in the array will be displayed in Content...
Content's state has a field called "html" which is what holds the HTML to be injected in string form. [you have to use dangerouslySetInnerHTML]
This concept works fine, and I have a version of it now. However, imagine you have a React components that you want to add to each blog post. Say you want to add the component into a specific blog post in a specific section. You want to add props to it and such. Now this goes out the window with dangerouslySetInnerHTML
This is where I am stuck trying to find the best direction to go. The only thought I have now is the following:
Since you would now be writing JSX in the XML, just make each blog post its own component. You would have ...etc and then if this.state.currentPost === 1 then display BlogPost1 and likewise. Yet you would have to have a huge block of if-statements depending on how many blogposts you have, and its not ideal to have to add everytime you have a new blogpost
When I read the title of your question I got curious and found this library to parse XML into React components: xml-to-react. But that's not what you are asking for.
As you want to use components in the middle of you string of HTML, I'll suggest: react-remarkable. This component compiles its children (a string with markdown/html/react) into react nodes.
Example from its docs:
var React = require('react');
var Markdown = require('react-remarkable');
var MyComponent = React.createClass({
render() {
return (
<div>
{/* Pass Markdown source to the `source` prop */}
<Markdown source="**Markdown is awesome!**" />
{/* Or pass it as children */}
{/* You can nest React components, too */}
<Markdown>{`
## Reasons React is great
1. Server-side rendering
2. This totally works:
<SomeOtherAmazingComponent />
Pretty neat!
`}</Markdown>
</div>
);
}
});
I am using an Image component as a background in React Native and am currently providing the source prop with an image as follows, which works.
const ImageWrapper = (props) => {
return (
<Image source={require('../../../images/testingImages/cells.png')} style={styles.imageView}>
{props.children}
</Image>
);
};
However, I would like to provide the require with an interpolated string with the name of an image provided by a prop as so:
require(`../../../images/testingImages/${props.imgURL}`)
But whenever I do so (even when I create the string as a seperate variable without using ES6 and pass it into require). I get the error -
"unknown named module '../../../images/testingImages/cells.png'".
Is there away to get the image without using require? As I would love to be able to pass the image url as a prop so that I can reuse the component when I want to change the background.
Any help is much appreciated!
Hopefully somebody else can provide a solution to your exact problem because I was having the same issue, but my workaround was to pass the entire value for source as a prop. I had that as a value to a certain key for each map within a list in my scenario, so that was clean enough for me. But that may be just moving the problem up a level in your case.
First create a file with image required - React native images must be loaded this way.
assets/index.js
export const leftChevron = require('./left-chevron.png');
export const rightChevron = require('./right-chevron.png');
export const circle = require('./oval-bottom-right.png');
export const homeandgarden = require('./homeAndGarden.png');
Now import all your assets
App.js
import * as All from '../../assets';
You can now use your image as an interpolated value where imageValue is the same as named local file:
<Image style={styles.image} source={All[`${imageValue}`]}></Image>
I am not sure if it solves your issue but if your image is available over network you can use your base URL to create a URL to the image and use that URL directly inside as a source.
var fileName = "trolltunga.jpg";
var imagesBaseUrl = "https://www.w3schools.com/css/";
var image = imagesBaseUrl+fileName;
const imageURL = {url:image}
class App extends React.Component {
render() {
return (
<Image source={imageURL} style={styles.imageView}>
</Image>
);
}
}
For demo check here https://rnplay.org/apps/hEYzcA
Here is how I got round it specifically, not really a perfect answer but works fro my purpose. I think the require is called when the component loads and hence the string doesn't get a chance to interpolate to be passed. So instead I import the relevant image in the parent component of image wrapper:
import jungle from '../../images/jungle.jpg';
Then pass as a prop to Image Wrapper
<ImageWrapper image={jungle} />
Then pass this prop to the image source component:
<Image source={this.props.image} style={styles.imageView}>
{ this.props.children }
</Image>
If I defined a ReactJS class in say dialog.js:
var divStyle = {
padding: '15px',
paddingBottom: '12px'
};
var Dialog = React.createClass({
render: function() {
return (
<div style={divStyle}>...</div>
);
}
});
In the above I define a class.. but in every example I see there is also the React.renderComponent(<Dialog/>,document.getElementById('someId'));
The problem I have with this is.. if I want to use this component on different pages with different IDs to render it at, or perhaps for several different IDs, I can't hard-code the id in the actual class it's at. I supposed I can pass in the ID in some manner??
But furthermore, I also want to be able to render the component in say a different JS class, one loaded AFTER the loading of this js class. I have an app.js class for my SPA app, that is loaded last. In it, when a user clicks a link, I may want to only render this component at that time. I do realize in the render method I can control in some way whether or not it's actually rendered.. but my thinking is also to not even bothering inserting it into the DOM unless an action occurs. Sort of like lazy insertion I guess?
I've tried in my app.js:
function () {
React.renderComponent(<Dialog/>,...);
}
but obviously this doesn't work as this JS is not a JSX JS file. So I tried using React.renderComponent(Dialog,...); thinking the Dialog class is globally defined, so it would be available, but that too fails.
So how do I use the Dialog class in another function to render it?
I think you're getting something conceptually wrong here:
but in every example I see there is also the React.renderComponent(<Dialog/>,document.getElementById('someId'));
The fact that the short examples are followed by a renderComponent call is for the trivial reason to show the demos. Once again, keep in mind that in React, <Dialog /> desugars to Dialog(). If you don't call it nothing happens.
So, simply don't call until you need it.
Also, I don't understand what you mean by:
but obviously this doesn't work as this JS is not a JSX JS file
Since you can just process that file through JSX?
If you're having trouble mentally mapping JSX to functions and vice-versa: try the live compiler here: http://facebook.github.io/react/jsx-compiler.html