File Preview in React FilePond not showing up when setting initial file - reactjs

I've been struggling quite a bit with React filepond lately so I just need some help. So, I'm able to upload photos to the server with react filepond with rails backend and amazon s3 storage. Everything works, no issue. Now I'm trying to set an initial image but I'm having issues getting the preview to work.
So, here I'm setting the initial image from a publicly accessible placeholder image:
class UserForm extends Component {
constructor(props) {
super(props);
this.classes = props.classes;
this.state = {
files: [{
source: "https://picsum.photos/200/300",
options: {
file: {
size: 200
}
}
}],
code omitted for brevity
and here's my FilePond component:
<FilePond
ref={ref => (this.pond = ref)}
files={this.state.files}
allowMultiple={true}
maxFiles={3}
oninit={() => this.handleInit()}
onupdatefiles={ this.fileChange }
allowImageCrop={true}
imageCropAspectRatio={'1:1'}
allowImageResize={true}
imageResizeTargetWidth={100}
imageResizeTargetHeight={100}
imageResizeUpscale={false}
imageResizeMode={'contain'}
allowImageTransform={true}
onpreparefile={ this.prepareFile }
So, its supposed to load the placeholder image in the preview but this is what I see instead:
Any configurations Im missing to get the preview to show up?
Thanks!
Edit 1
Added code sandbox link. Note that if I remove the options, specifically the file size, it will throw a warning saying that its waiting for the size
https://codesandbox.io/s/react-filepond-live-demo-8s7oc?file=/src/index.js
And here's the docs for setting up initial files in filepond: https://pqina.nl/filepond/docs/patterns/api/filepond-object/#setting-initial-files

I figured it out!
Here's a working code sandbox link:
https://codesandbox.io/s/react-filepond-live-demo-2sdn0?file=/src/index.js
you need to set the server config like this:
<FilePond
files={files}
allowMultiple={false}
server={{
load: (source, load, error, progress, abort, headers) => {
var myRequest = new Request(source);
fetch(myRequest).then(function(response) {
response.blob().then(function(myBlob) {
load(myBlob);
});
});
}
}}
labelIdle='Drag & Drop your files or <span class="filepond--label-action">Browse</span>'
/>
Credit goes to this asnwer from this link:
https://github.com/pqina/filepond/issues/192

Related

Getting "Unsafe attempt to load URL data:image/svg+xml..." in Safari using a through React component

I'm using rollup to bundle a react npm package that contains an icon component that takes a name as a prop and returns an Icon with that name wrapped by a react component.
This is the component code:
import sprite from './public/sprite.svg';
function Icon({ name }) {
return <svg className="svg-wrapper">
<use href={`${sprite}#${name}`} />
</svg>
);
}
The folder structure is the following:
- src
- - public
- - - sprite.svg
- - icons
- - - some-icon.svg
- - - some-other-icon.svg
- - index.tsx # component with the code mentioned above
And this is my rollup config:
export default {
plugins: [
esbuild({
sourceMap: false,
target: "esnext"
}),
image(),
svgicons({
inputFolder: "src/icons",
output: "public/sprite.svg"
}),
json()
]
}
This works fine in Chrome (although it does inline all the svg inside of the href which I think it's the purpose of this approach) but in Safari it triggers the following error:
Unsafe attempt to load URL data:image/svg+xml,%3c%3fxm ....
Domains, protocols and ports must match.
The thing is, as mentioned, this is an npm package that packages the icons as part of the js bundle (inlining) so there's not much control over how this component is served since this is handled by the browser caching (also one of the key points of using this approach). I'm quite familiar with CORS and I know that perhaps avoiding to use data:image/svg+xml uri links would fix this but would increase the complexity of the build steps of this package (needing to build the icons using svgr/svgo and then have some kind of lookup table to give back the right icon based on the name prop i.e.).
So, ultimately my question is, with the sprite approach in a react component library is there a foolproof way of avoiding these kind of issues and cross-browser inconsistencies?
Thanks in advance for any help provided.
I have been struggling with this issue for a while. I guess this is a bug on Safari throwing an error because is dealing with a dataURI as if it was an external URL.
About your code, you could expose your sprite in a public folder or publish it in a cdn (good for caching purposes) and change the way rollup is handling your svg (it seems it is packing your svg as a dataURI). Alternatively, I implemented a workaround to convert the dataURI in a blob.
import sprite from './public/sprite.svg';
function dataURItoBlobUrl(dataURI: string) {
const svg = decodeURI(dataURI).split(',')[1];
const blob = new Blob([svg], { type: "image/svg+xml" });
return URL.createObjectURL(blob);
}
const blobUrl = dataURItoBlobUrl(sprite);
export const Icon: FC<IconProps> = ({ name, ...props }) => {
return (
<svg xmlns="http://www.w3.org/2000/svg" {...props}>
<use href={`${blobUrl}#${name}`}></use>
</svg>
);
};

How can I load external images in Next.js?

JavaScript and I am loading random images from https://picsum.photos but it is not working. Note that for now, I do not want to use the new image optimization in next.js.
Local images are working but external images are not working.
Below is my code and my next.config.js
<Slider
{...settingsThumbs}
asNavFor={nav1}
ref={slider => setSlider2(slider)}
>
{slidesData.map(slide => (
<div className='slick-slide' key={slide.id}>
<img
className='slick-slide-image'
src={`https://picsum.photos/800/400?img=${slide.id}`}
alt='sfd'
/>
</div>
))}
</Slider>
below is my next.js config
module.exports = {
images: {
domains: ['https://picsum.photos/']
}
};
Per the docs, the domain does not appear to need a protocol (https or http) in front of it. Try
module.exports = {
images: {
domains: ['picsum.photos']
}
};
I also suspect you have an error like the following in your console, going off this source code:
domains value must follow format { domain: 'picsum.photos', ... }.
See more info here: https://err.sh/next.js/invalid-i18n-config`
Note: Modifications to the next.config.js file may sometimes require to to restart the dev server before they take effect.

visual studio react image path cannot reach

So i have a visual studio project created with react.js.
I am trying to link to an image dynamically, but failing. This is the code I am trying:
At the top im setting a variable for the first part of the path:
this.LogoPath = '../images/'
And then im dynamically grabbing the name of the image from an api call.
this.setState({ imageNamePath: this.state.location.imageName })
And then im concatinating them:
{`${this.LogoPath}${this.state.location.imageName}`}
In the console, im getting:
img src='../images/the-images-name-here.png'
So it seems to be working, but it is not. I get no errors, and I have broken images. My best guess is that react is changing the images to something like:
image-name-here.6a131799.png
Surely someone has run across this before, but my google search pulled up little help.
So how do i get the images to show?
Webpack is a smart tool. One of it's strength is the trash code/files elimination from the bundle.
That means that if a file is not imported using import myFile from '../myPath/myFile.jpg'; or require('../myPath/myFile.jpg');` it won't be a part of the final bundle.
In your case you're not importing the file. You're creating a string variable instead which means nothing to webpack.
There are different options that could work in your case:
Option 1: Pre-import all images and map them in an object:
import React, {Component} from 'react';
import image1 from '../assets/image1.png';
import image2 from '../assets/image2.png';
import image3 from '../assets/image3.png';
const imageTypes = {
image1,
image2,
image3,
}
export default class MyComponent extends Component {
constructor(props) {
super(props);
this.state = {
imageType: 'image1'
}
}
render() {
return (
<img src={imageTypes[this.state.imageType]} />
)
}
}
Option 2: Not recommended - Use require to dynamically import files (webpack configurations might be needed in order to include all possible required images in the production bundle):
class MyComponent {
constructor(props) {
super(props);
this.state = {
image: 'file1.png'
}
}
render() {
return (
<img src={require(`./assets/${this.state.image}`)} />
)
}
}
Option 3: Send the image blob (in base64) from the API.
I suggest you to use the Option 1 or Option 3, based on your requirements, such as: how often will be images be changed/added/removed. It's not normal to import files dynamically from ReactJS bundle and you might end up having a non-existing image in your project requested by the data coming from an external source.
For Option 2 you also might have some problems configuring the webpack in order to include in the bundle all the images that you'll probably need to render, even though they are not imported (hardcoded) somewhere in the JS files. Keep in mind that the React Application in production ends up being a collection of static files. You'll need to treat them as follows.

react native (expo) load markdown files

I'm having some troubles loading markdown files (.md) into my react native (non-detached expo project).
Found this awesome package that allows me to render it. But can't figure out how to load the local .md file as a string.
import react from 'react';
import {PureComponent} from 'react-native';
import Markdown from 'react-native-markdown-renderer';
const copy = `# h1 Heading 8-)
| Option | Description |
| ------ | ----------- |
| data | path to data files to supply the data that will be passed into templates. |
| engine | engine to be used for processing templates. Handlebars is the default. |
| ext | extension to be used for dest files. |
`;
export default class Page extends PureComponent {
static propTypes = {};
static defaultProps = {};
render() {
return (
<Markdown>{copy}</Markdown>
);
}
}
BTW: I tried googling, but can't get the suggestions to work
https://forums.expo.io/t/loading-non-media-assets-markdown/522/2?u=norfeldtconsulting
I tried the suggested answers for reactjs on SO, but the problem seems to be that it only accepts .js and .json files
Thanks to #Filipe's response, I got some guidance and got a working example that will fit your needs.
In my case, I had a .md file on the assets/markdown/ folder, the file is called test-1.md
The trick is to get a local url for the file, and then use the fetch API to get its content as a string.
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import Markdown from 'react-native-markdown-renderer';
const copy = `# h1 Heading 8-)
| Option | Description |
| ------ | ----------- |
| data | path to data files to supply the data that will be passed into templates. |
| engine | engine to be used for processing templates. Handlebars is the default. |
| ext | extension to be used for dest files. |
`;
export default class App extends React.Component {
constructor(props) {
super(props)
this.state = {
copy: copy
}
}
componentDidMount() {
this.fetchLocalFile();
}
fetchLocalFile = async () => {
let file = Expo.Asset.fromModule(require("./assets/markdown/test-1.md"))
await file.downloadAsync() // Optional, saves file into cache
file = await fetch(file.uri)
file = await file.text()
this.setState({copy: file});
}
render() {
return (
<Markdown>{this.state.copy}</Markdown>
);
}
}
EDIT: In order to get get rid of the error
Unable to resolve "./assets/markdown/test-1.md" from "App.js"
you would need to add the packagerOpts part of #Filipe's snippet into your app.json file.
app.json
{
"expo": {
...
"assetBundlePatterns": [
"**/*"
],
"packagerOpts": {
"assetExts": ["md"]
},
...
}
}
EDIT 2:
Answering to #Norfeldt's comment:
Although I use react-native init when working on my own projects, and I'm therefore not very familiar with Expo, I got this Expo Snack that might have some answers for you: https://snack.expo.io/Hk8Ghxoqm.
It won't work on the expo snack because of the issues reading non-JSON files, but you can test it locally if you wish.
Using file.downloadAsync() will prevent the app making XHR calls to a server where your file is hosted within that app session (as long as the user does not close and re-open the app).
If you change the file or modify the file (simulated with a call to Expo.FileSystem.writeAsStringAsync()), it should display the updated as long as your component re-renders and re-downloads the file.
This will happen every time your app is closed and re-open, as the file.localUri is not persisted per sessions as far as I'm concerned, so your app will always call file.downloadAsync() at least once every time it's opened. So you should have no problems displaying an updated file.
I also took some time to test the speed of using fetch versus using Expo.FileSystem.readAsStringAsync(), and they were on average the same. Often times Expo.FileSystem.readAsStringAsync was ~200 ms faster, but it 's not a deal breaker in my opinion.
I created three different methods for fetching the same file.
export default class MarkdownRenderer extends React.Component {
constructor(props) {
super(props)
this.state = {
copy: ""
}
}
componentDidMount() {
this.fetch()
}
fetch = () => {
if (this.state.copy) {
// Clear current state, then refetch data
this.setState({copy: ""}, this.fetch)
return;
}
let asset = Expo.Asset.fromModule(md)
const id = Math.floor(Math.random() * 100) % 40;
console.log(`[${id}] Started fetching data`, asset.localUri)
let start = new Date(), end;
const save = (res) => {
this.setState({copy: res})
let end = new Date();
console.info(`[${id}] Completed fetching data in ${(end - start) / 1000} seconds`)
}
// Using Expo.FileSystem.readAsStringAsync.
// Makes it a single asynchronous call, but must always use localUri
// Therefore, downloadAsync is required
let method1 = () => {
if (!asset.localUri) {
asset.downloadAsync().then(()=>{
Expo.FileSystem.readAsStringAsync(asset.localUri).then(save)
})
} else {
Expo.FileSystem.readAsStringAsync(asset.localUri).then(save)
}
}
// Use fetch ensuring the usage of a localUri
let method2 = () => {
if (!asset.localUri) {
asset.downloadAsync().then(()=>{
fetch(asset.localUri).then(res => res.text()).then(save)
})
} else {
fetch(asset.localUri).then(res => res.text()).then(save)
}
}
// Use fetch but using `asset.uri` (not the local file)
let method3 = () => {
fetch(asset.uri).then(res => res.text()).then(save)
}
// method1()
// method2()
method3()
}
changeText = () => {
let asset = Expo.Asset.fromModule(md)
Expo.FileSystem.writeAsStringAsync(asset.localUri, "Hello World");
}
render() {
return (
<ScrollView style={{maxHeight: "90%"}}>
<Button onPress={this.fetch} title="Refetch"/>
<Button onPress={this.changeText} title="Change Text"/>
<Markdown>{this.state.copy}</Markdown>
</ScrollView>
);
}
}
Just alternate between the three to see the difference in the logs.
From what I know, this cannot be done within expo. I use react-native and run it on my mobile for development.
react-native use Metro as the default bundler, which also suffers from similar problems. You have to use haul bundler instead.
npm install --save-dev haul
npx haul init
npx haul start --platform android
In a seperate terminal run react-native run-android. This would use haul instead of metro to bundle the files.
To add the markdown file, install raw-loader and edit the haul.config.js file. raw-loader imports any file as a string.
Customise your haul.config.js to look something like this:
import { createWebpackConfig } from "haul";
export default {
webpack: env => {
const config = createWebpackConfig({
entry: './index.js',
})(env);
config.module.rules.push({
test: /\.md$/,
use: 'raw-loader'
})
return config;
}
};
Now you can import the markdown file by using const example = require('./example.md')
Haul supports webpack configuration so you can add any custom babel transform you want.
I don't know exactly where the problem lies, but I added html files to the project, and I'd imagine it would be very similar.
Inside your app.json, try adding these fields:
"assetBundlePatterns": [
"assets/**",
],
"packagerOpts": {
"assetExts": ["md"]
},
The packagerOpts makes it so the standalone will bundle the .md files. I'd imagine you already have an assets folder, but just in case you don't, you will need one.
Then, on AppLoading, loading the assets with Asset.loadAsync might not be needed, but it's a good idea to rule out. Check out the documentation on how to use it.
When importing the file, there are three ways you might want to do so, that change depending on the environment. I'll copy this excerpt from my Medium article:
In the simulator, you can access any file in the project. Thus, source={require(./pathToFile.html)} works. However, when you build a standalone, it doesn’t work quite in the same way. I mean, at least for android it doesn’t. The android webView doesn’t recognise asset:/// uris for some reason. You have to get the file:/// path. Thankfully, that is very easy. The assets are bundled inside file:///android_asset (Careful, don’t write assets), and Expo.Asset.fromModule(require(‘./pathToFile.html')).localUri returns asset:///nameOfFile.html. But that’s not all. For the first few times, this uri will be correct. However, after a while, it changes into another file scheme, and can’t be accessed in the same way. Instead, you’ll have to access the localUri directly. Thus, the complete solution is:
/* Outside of return */
const { localUri } = Expo.Asset.fromModule(require('./pathToFile.html'));
/* On the webView */
source={
Platform.OS === ‘android’
? {
uri: localUri.includes('ExponentAsset')
? localUri
: ‘file:///android_asset/’ + localUri.substr(9),
}
: require(‘./pathToFile.html’)
}
(A constant part of the uri is ExponentAsset, that’s why I chose to check if that was part of it)
That should probably solve your problem. If it doesn't, comment what's going wrong and I'll try to help you further. Cheers!
If you want to load .md file with react-native cli (without expo). I've got a solution for you)
Add https://github.com/feats/babel-plugin-inline-import to your project
Add config .babelrc file with code inside:
{
"presets": ["module:metro-react-native-babel-preset"],
"plugins": [
[
"inline-import",
{
"extensions": [".md", ".txt"]
}
],
[
"module-resolver",
{
"root": ["./src"],
"alias": {}
}
]
]
}
Add to your metro.config.js such code
const metroDefault = require('metro-config/src/defaults/defaults.js');
...
resolver: {
sourceExts: metroDefault.sourceExts.concat(['md', 'txt']),
}
....
Reload your app

How to check if asset was added from public/ dir in React?

Is it possible to check if a file exists within the /public/ directory?
I have a set of images that correspond to some objects. When available, I would like to display them using <img> tag. However not all of the objects have a corresponding image, in which case I would like to perform a REST request to our server.
I could create a list of files as part of build process, but I would like to avoid that if possible.
I am using create-react-app if it matters (if I understand correctly fs doesn't work in client-side React apps).
EDIT: I guess I should have been more exact in my question - I know client-side JS can't access this information (except through HTTP requests), I was just hoping something saves information (during build) about the files available in a way that is accessible to client-side Javascript... Maybe Webpack or some extension can do this?
You can do this with your axios by setting relative path to the corresponding images folder. I have done this for getting a json file. You can try the same method for an image file, you may refer these examples
Note: if you have already set an axios instance with baseurl as a server in different domain, you will have to use the full path of the static file server where you deploy the web application.
axios.get('http://localhost:3000/assets/samplepic.png').then((response) => {
console.log(response)
}).catch((error) => {
console.log(error)
})
If the image is found the response will be 200 and if not, it will be 404.
Edit: Also, if the image file is present in assets folder inside src, you can do a require, get the path and do the above call with that path.
var SampleImagePath = require('./assets/samplepic.png');
axios.get(SampleImagePath).then(...)
First of all you should remember about client-server architecture of any web app. If you are using create-react-app you are serving your app via webpack-dev-server. So you should think about how you will host your files for production. Most common ways are:
apache2 / nginx
nodejs
but there is a lot of other ways depending on your stack.
With webpack-dev-server and in case you will use apache2 / nginx and if they would be configured to allow direct file access - it is possible to make direct requests to files. For example your files in public path so
class MyImage extends React.Component {
constructor (props) {
super(props);
this.state = {
isExist: null
}
}
componentDidMount() {
fetch(MY_HOST + '/public/' + this.props.MY_IMAGE_NAME)
.then(
() => {
// request status is 200
this.setState({ isExist: true })
},
() => {
// request is failed
this.setState({ isExist: false })
}
);
}
render() {
if (this.state.isExist === true) {
return <img src={ MY_HOST + "/public/" + this.props.MY_IMAGE_NAME }/>
}
return <img src="/public/no-image.jpg"/>
}
}

Resources