webpack + mobx + React : require an observable array of images - arrays

I am using mobX to store arrays of images for a gallery like this :
#observable peopleThumb = [
'src/assets/photos/people/thumbs_1.jpg',
'src/assets/photos/people/thumbs_2.jpg',
'src/assets/photos/people/thumbs_3.jpg',
etc...
]
Where I have my thumbnails array, the full images array and the mobile images array.
Then I map through the array and use them either as background images or in an image tag in my components (urls are passed down via props) :
<img src={url} />
or
background: `url(${url}) no-repeat center center`,
But when I build for prod since the images are not required and the urls stays like src/assets/photos/people/' they obviously won't be displayed
So I tried simply requiring them :
<img src={require(url)}/>
and
background: `url(${require(url)}) no-repeat center center`
But I get an error :
Uncaught Error: Cannot find module 'src/assets/photos/people/full_3.jpg'.
I tried changing the path in my store to a relative path :
#observable peopleThumb = [
'../assets/photos/people/thumbs_1.jpg',
'../assets/photos/people/thumbs_2.jpg',
'../assets/photos/people/thumbs_3.jpg',
etc...
]
And same error :
Uncaught Error: Cannot find module '../assets/photos/people/full_2.jpg'.
My webpack config for my output and the file loader :
output: {
path: "./build/",
filename: 'static/js/[name].[chunkhash:8].js',
chunkFilename: 'static/js/[name].[chunkhash:8].chunk.js'
},
{
test: /\.(jpg|png|svg)(\?.*)?$/,
loader: 'file',
query: {
name: 'static/media/[name].[hash:8].[ext]'
}
},
When I import single images via Import everything works fine but I am stuck when it comes to this observable. I also tried the require context but I honestly didn't really understood what I was doing and I think I should be able to simply require my images once mapped.
Thanks.

Related

React.js. How to pass local images dynamicly according to id property?

I have a little problem. I don't know how to pass this local images dynamicly.
Lets say I have API which returns static 6 store schema like that.
stores: [{id:1, name: "Wallmart"}, {id: 2, name: "Amazon"}];
and I have some images called store1.jpg, store2.jpg ...etc.
So I import images like that.
import store1 from "./store1.jpg"
So I need to loop these images dynamicly according to id.
{
stores.map((store) => (
<img src={`store${store.id}`} />
)
}
So, My question is what is best way to do that. maybe I can create some object which contains all images or import all images like that. I didn't check this version maybe it's not working at all. It doesn't looks like proper way. Can someone just advice me something who have experinced with that ?
Forget about importing the images, and just downright use the path to the images in the stores.map function.
<img src={"store" + store.id + ".jpg"} />
I found this type of solution because I figure out that I also have colors. I add all my images into public/images folder
export const stores : any = {
store1:{
id: 1,
src: "/images/store1.png",
tertiary: "#000",
primary: "#FFF",
},
store2:{
id: 2,
src: "/images/store2.png",
tertiary: "#999",
primary: "#aaa",
}
...
}
so I import stores and loop like that.
import {stores} from "./constants"
{
store.map((item:any) => (
<StoreCatalogCard
title={item.name}
img={stores[`store${item.id}`].src}
primaryColor={stores[`store${item.id}`].primary}
tertiaryColor={stores[`store${item.id}`].tertiary}
light={true}
/>
))
}

I'm having this weird webpack.cache.PackageCacheStrategy

Below is the error on my console
$ next dev
ready - started server on 0.0.0.0:3000, url: http://localhost:3000
info - Using webpack 5. Reason: no custom webpack configuration in next.config.js https://nextjs.org/docs/messages/webpack5
warn - You have enabled the JIT engine which is currently in preview.
warn - Preview features are not covered by semver, may introduce breaking changes, and can change at any time.
<w> [webpack.cache.PackFileCacheStrategy] Skipped not serializable cache item 'Compilation/modules|C:\Users\J.Andrew\Documents\WebDev\nextjs-boilerplate\node_modules\next\dist\compiled\css-loader\cjs.js??ruleSet[1].rules[2].oneOf[7].use[1]!C:\Users\J.Andrew\Documents\WebDev\nextjs-boilerplate\node_modules\next\dist\compiled\postcss-loader\cjs.js??ruleSet[1].rules[2].oneOf[7].use[2]!C:\Users\J.Andrew\Documents\WebDev\nextjs-boilerplate\node_modules\next\dist\compiled\resolve-url-loader\index.js??ruleSet[1].rules[2].oneOf[7].use[3]!C:\Users\J.Andrew\Documents\WebDev\nextjs-boilerplate\node_modules\next\dist\compiled\sass-loader\cjs.js??ruleSet[1].rules[2].oneOf[7].use[4]!C:\Users\J.Andrew\Documents\WebDev\nextjs-boilerplate\styles\globals.scss': No serializer registered for CssSyntaxError
<w> while serializing webpack/lib/cache/PackFileCacheStrategy.PackContentItems -> webpack/lib/NormalModule -> webpack/lib/ModuleBuildError -> CssSyntaxError
error - ./styles/globals.scss:1:1
Syntax error: Unknown word
wait - compiling...
error - ./styles/globals.scss:1:1
Syntax error: Unknown word
Here is global.scss mentioned in the error. When I try to removed the tailwind imports, it compiles without a problem. But I needed those in order for tailwind to work.
#import '~tailwindcss/base';
#import '~tailwindcss/components';
#import '~tailwindcss/utilities';
html,
body {
padding: 0;
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
}
a {
color: inherit;
text-decoration: none;
}
* {
box-sizing: border-box;
}
My tailwind.config.js
module.exports = {
mode: 'jit',
purge: ['./pages/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}'],
darkMode: false, // or 'media' or 'class'
important: true,
theme: {
container: {
center: true,
padding: '1.5rem',
},
extend: {
colors: {
// 'nav-bg': '#383E4C',
},
},
},
variants: {
extend: {},
},
plugins: [require('#tailwindcss/forms')],
}
And my postcss.config.js which is default
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
Please help...
Today I had the same problem. My code was working just fine, until I created a React component that had a function that uses querySelector, something like this:
const handleSomething = () => {
const x = 150;
let activeSlide = document.querySelector('.slide');
activeSlide.classList.add(`-translate-x-[${x}]`);
}
It seemed that the -translate-x-[${x}] statement was causing the bug. However, after removing this line, the problem didn't go away. I tried to delete the "node_modules" and ".next" folders and reinstall the dependencies, but nothing seemed to work.
I don't know what caused it, but the only way I could get the application to run again was to go back to the previous commit (with a git reset --hard HEAD - WARNING: be careful with this command because you loose all uncommitted changes, but that was my intention) and delete that component (the file itself). Even a simple copy and paste of the contents of this file, with most of the "weird" lines commented out (this function, basically), would make the error come back. Literally nothing else seemed to work for me.
It probably doesn't answer your question, but I hope it can help someone who is facing the same problem, until no better solution comes up.
As Renato mentioned in his answer, it seems dynamically constructing tailwind class names returns this error.
This is explained in the tailwind docs here:
Dynamic class names
As outlined in Class detection in-depth, Tailwind doesn’t actually run your source code and won’t detect dynamically constructed class names.
❌ Don't construct class names dynamically
<div class="text-{{ error ? 'red' : 'green' }}-600"></div>
✔️ Always use complete class names
<div class="{{ error ? 'text-red-600' : 'text-green-600' }}"></div>
Also from this documentation about how tailwind detects CSS class names:
The most important implication of how Tailwind extracts class names is that it will only find classes that exist as complete unbroken strings in your source files.
If you use string interpolation or concatenate partial class names together, Tailwind will not find them and therefore will not generate the corresponding CSS
Therefore to dynamically set a CSS property of an element, using the inline style provided by React.js would be the best way to do it. For example:
const divStyle = {
color: 'blue',
backgroundImage: 'url(' + imgUrl + ')',
};
function HelloWorldComponent() {
return <div style={divStyle}>Hello World!</div>;
}
I had a similar issue that happened when I tried to add a preview of the filed I just choosed.
For that I used the URL object like that :
image.src = URL.createObjectURL(picture)
I just remove this part of my component the error was gone.

NextJS + React + SVG Component - Import Typescript IntrinisicAttribues error

I'm trying to directly load SVG elements into a React component, and it's been a massive headache. I'm a bit new to really digging in with understanding. To be fair, I'm really enjoying Next, it reminds me a lot of Ruby or Laravel.
So, perhaps someone can help guide me on the path of enlightenment here... I'm using a Next plugin, withReactSvg
next.config.js
module.exports = withPlugins(
[
[
withReactSvg, {
include: path.resolve(__dirname, 'lib/svg/'),
webpack(config, options) {
return config
}
}
],
//[/*plug name*/, { /* plugin config here ... */ }],
],
{
/* global config here ... */
future: {
webpack5: true,
},
distDir: '.build',
},
);
This plugin extends Next to import SVGs more directly and be able to control internal SVG css more directly, and it's pretty elegant compared with other solutions...
In any component, I can now do the following:
ExampleComponent.tsx
import Icon from '../lib/svg/icon_4.svg';
export default function ExampleComponent() {
return ( <Icon fill="red" stroke="white" /> );
}
This code compiles and works, but trying to use Typescript I receive this error:
(JSX attribute) fill: string
Type '{ fill: string; stroke: string; }' is not assignable to type 'IntrinsicAttributes'.
Property 'fill' does not exist on type 'IntrinsicAttributes'.ts(2322)
The closest I think I've gotten to solving this puzzle is a really good article React Higher Order Component Patterns In Typescript, but it comes just shy of working because I'm sort of implicitly creating a react component with the import.
A lot of other articles seem to assume I'm trying to do something I shouldn't be doing... And maybe I am?

How can I display GIF images efficiently in my Gatsby blog website?

A few days ago, I bought a Gatsby blog theme and tried to modify it. The blog site uses Images(PNG, JPEG), not animated GIFs. So I tried to use GIF images for all blog posts but it affected site performance.
Also, I notice that Gatsby Image doesn't provide a GIF format. How can I use GIF on my blog with high performance?
You can convert GIFs into MP4 videos with H.264 encoding using ffmpeg. Then use <video src="..." /> in place of your img tag. To make this really easy, I have a React component that I use for this that includes automatic playback when the video is visible:
import React, { useEffect } from "react"
import PropTypes from "prop-types"
import { useInView } from "react-intersection-observer"
const GifVideo = ({ threshold = 0.15, ...playerProps }) => {
const [ref, inView] = useInView({ threshold })
useEffect(() => {
if (inView) {
ref.current?.play()
} else {
ref.current?.pause()
}
}, [ref, inView])
return <video ref={ref} autoPlay playsInline muted loop {...playerProps} />
}
export default GifVideo
GifVideo.propTypes = {
src: PropTypes.string,
threshold: PropTypes.number,
className: PropTypes.string,
}
Then to you use it, it's this easy:
<GifVideo src="/your/video.mp4" width={400} className="some-class" />
For what it's worth, I don't recommend using the sharp-backed GraphQL image transformers in Gatsby (gatsby-transformer-sharp). It's exceedingly slow, couples the presentation to the query, and doesn't provide any way to handle art direction.
I use gatsby-remark-interactive-gifs plugin to show gifs on my gatsby blog.
Install gatsby-remark-interactive-gifs
npm install --save gatsby-remark-interactive-gifs
yarn add gatsby-remark-interactive-gifs
Add this config to gatsby-config.js:
{
resolve: `gatsby-transformer-remark`,
options: {
plugins: [
{
resolve: `gatsby-remark-interactive-gifs`,
options: {
root: `${__dirname}`,
src: `${__dirname}/content/gif`,
dest: `${__dirname}/public/static/gifs`,
play: `${__dirname}/src/img/play.gif`,
placeholder: `${__dirname}/src/img/play.gif`,
loading: `${__dirname}/src/img/play.gif`,
relativePath: `/static/gifs`,
},
},
],
},
},
From plugin document:
root - The root of your project.
src - Where all the gifs you want processed are stored. Absolute path.
dest - A path in public where your gifs are stored. Absolute path.
play - An image to indicate that the gif can be interacted with. Absolute path.
placeholder - An image to show when the gif is missing in action. Absolute path.
loading - An image which shows when the gif is downloading. Absolute path.
relativePath - The relative path served from public/.
! Make sure you are adding this above the prismjs config.
Sample code in MD file to show gifs on your gatsby blog:
<img src="/static/gifs/fileName.gif">

Module scss ampersand class selector not working

I have a simple scss file with
.navItem {
cursor: pointer;
&.active {
color: $primaryPurple;
}
&:hover {
color: $primaryPurple;
}
span {
margin-left: 10px;
}
}
For some reason, the :hover works but .active doesn't work. As you can see in the image below, li clearly has the active class but I don't see the font color css changed.
TL;DR: Pass the name as a JavaScript Object, not like a simple string.
Actually, this issue comes from the css-modules configuration. Obviously, based on the AdminSideBar_navItem__1aFBc in the embed picture. your css-modules configuration in the Webpack config is:
// webpack config
module.exports = {
~~~
module: {
~~~
rules: [
{
test: /\.scss$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [
{
loader: 'css-loader',
options: {
modules: true,
importLoaders: 1,
localIdentName: '[name]_[local]_[hash:base64:5]', // <=== Attention to this line
sourceMap: true,
}
},
~~~
],
}),
],
},
~~~
};
So, when you are using css-modules it means you will have so professional advantages like a more compressed css bundle file or full hashed unreadably uglified bundle. for these benefits, you should use CSS class names like a JavaScript object, pay attention to the following ReactJS code alongside using css-modules:
import React from 'react';
import cn from 'classnames'; // <=== install it to have easy coding
import styles from '[pathToScssFiles]/styles.scss';
const MyComponent = ({ isActive }) => (
<li className={cn(styles.navItem, isActive && styles.active)} />
);
I hope, you understand my point, you passed the styles.active as a simple string: "active". so css-modules cannot get your class name as an object and pass it exactly like what it got, as a simple string, hence, "active" **BUT** in the CSS bundle file the css-modulesuse the'[name][local][hash:base64:5]'` pattern to deform its name.
Your :hover should work and it works because anyone cannot change it, it's a pseudo-class.
For sure, write a weird property for .active:
.navItem {
cursor: pointer;
&.active {
fill: yellow;
}
Now seek for the fill: yellow in the CSS bundle that Webpack makes for you. you see it is:
.AdminSideBar_navItem__1aFBc .AdminSideBar_active__9be2a {
fill: yellow;
}
Note: the ~~~ means etc.
Providing you jsx code would help, but I will take a guess that you use active as a string literal, so your code looks like
import styles from './componentName.module.css';
// and then in the component
<li className={`nav-item ${styles.AdminSideBar_navItem} ${whateverCondition ? 'active' : ''}`}>
...children...
</li>
The problem here is that active class also has a suffix just like AdminSideBar_navItem. And in order to make it work you need to access it the same way you access AdminSideBar_navItem. So the code should be
<li className={`nav-item ${styles.AdminSideBar_navItem} ${whateverCondition ? styles.active : ''}`}>
...children...
</li>
and in html you will see
<li class="nav-item AdminSideBar_navItem_abcd active_bcde">
I dropped a simple example in this codesandbox, you can play around with it and see how it works.

Resources