Why my images converted to string when submitting post request? - javascript

see the screenshot . Why images getting corrupted? I am trying to upload images using axois post but axois post can't processing my images correctly . My code divided in two part. First part where I writing logic for upload multi image and second part I am using it in my page component.
first part
this code for upload multi image
export const MultiImageUpload = ({Setimage}) => {
const [selectedImages, setSelectedImages] = useState([]);
const onSelectFile = (event) => {
const selectedFiles = event.target.files;
const selectedFilesArray = Array.from(selectedFiles);
const imagesArray = selectedFilesArray.map((file) => {
return URL.createObjectURL(file);
});
setSelectedImages((previousImages) => previousImages.concat(imagesArray));
Setimage((previousImages) => previousImages.concat(imagesArray));
// FOR BUG IN CHROME
event.target.value = "";
};
function deleteHandler(image) {
setSelectedImages(selectedImages.filter((e) => e !== image));
Setimage(selectedImages.filter((e) => e !== image));
URL.revokeObjectURL(image);
}
second part
now I am importing this component in my page
const AdsPost = ({data}) => {
const[image,Setimage] = useState([])
var data = new FormData();
image.forEach(file=>{
data.append("files", file)
console.log("image_url:",file)
})
let submit_ads = axios.post(url,data,{headers:headers}).then((res)=>{
console.log(res)
})
here is myjsx
<MultiImageUpload
Setimage={Setimage}/>
I can upload image using postman but don't know why axois post can't upload images.

Here is your problem :
image.forEach(file=>{
data.append("files", file)
console.log("image_url:",file)
})
The parameter file is not a file, but it's the result of
const imagesArray = selectedFilesArray.map((file) => {
return URL.createObjectURL(file); // <--- this line
});
In other words, you are essentially doing
data.append("files", URL.createObjectURL(file));
Fix that and your code should work.
Solution
Here is a sandbox with a proposed solution, the idea is to delegate the state of the files to a Provider, and use the context down in child components as needed.
./context/files.js
import { createContext } from "react";
export default createContext({
/** #return {{ file:File, dataUrl:string }[]]} */
get files() {
return [];
},
/** #return {Error[]} */
get errors() {
return [];
},
/** #param {File[]} files */
addFiles(files) {},
/** #param {File} file */
removeFile(file) {},
/** #param {Error[]} errors */
setErrors(errors) {}
});
./providers/FilesProvider.jsx
import { useContext, useMemo, useState } from "react";
import FilesContext from "../context/files";
const FilesProvider = ({ children }) => {
const [internal, setInternal] = useState(() => ({
files: [],
errors: []
}));
const contextValue = useMemo(
() => ({
get files() {
return internal.files;
},
get errors() {
return internal.errors;
},
addFiles(filesAdded) {
setInternal(({ files, errors }) => ({
files: files.concat(
filesAdded.map((file) => ({
file,
dataUrl: URL.createObjectURL(file)
}))
),
errors
}));
},
removeFile(fileRemoved) {
URL.revokeObjectURL(fileRemoved);
setInternal(({ files, errors }) => ({
files: files.filter(({ file }) => file !== fileRemoved),
errors
}));
},
setErrors(errors) {
setInternal(({ files }) => ({ files, errors }));
}
}),
[internal]
);
return (
<FilesContext.Provider value={contextValue}>
{children}
</FilesContext.Provider>
);
};
const useFiles = () => useContext(FilesContext);
export default FilesProvider;
export { useFiles };
Usage
<FilesProvider>
<FilesSelectComponent />
</FilesProvider>
and
const { files, errors, addFiles, removeFile, setErrors } = useFiles();

Related

React: I believe I have a GOTCHA for a double async fetch call. I click the button once part of the setState renders I click again and then it renders

Question:
I am developing a small app that is a memory game of Formula One Drivers to practice React. It makes a call to an API to get the driver info then I have to make a second API call to Wikipedia to get the driver images. When I submit the year and click the button it will only load half the information Image 1 & getDrivers function. When I click the button again it will load the images Image 2 & getDriversImgs function / retrievingImgUrl.
I believe I am encountering a GOTCHA or doing something fundamentally wrong. I am not sure in my setDrivers call in the retrievingImgUrl() function if it isn't updating because it is a reference to an array even though I use map and it should be returning a new array?
Or is this something where I need to use useEffect or useCallback to have the code rerender in one go?
Any advice on how to fix the bug and if you could point me in a direction to possibly clean up these fetch calls or would you consider this clean code (like conceptually chaining fetch calls together in smaller functions or should I make it one big function)?
import { Fragment, useState, useEffect } from "react";
// Components
import Header from "./components/header/Header";
import CardList from "./components/main/CardList";
import Modal from "./components/UI/Modal";
// CSS
import classes from "./App.module.css";
function App() {
const [drivers, setDrivers] = useState([]);
const getDrivers = async (year) => {
const response = await fetch(
"https://ergast.com/api/f1/" + year + "/drivers.json"
);
const data = await response.json();
let driverInfo = [];
data.MRData.DriverTable.Drivers.map((driver) => {
driverInfo.push({
id: driver.code,
firstName: driver.givenName,
lastName: driver.familyName,
wikipedia: driver.url,
image: null,
});
});
setDrivers(driverInfo);
getDriversImgs();
};
async function getDriversImgs() {
console.log(drivers);
const responses = await Promise.all(
drivers.map((driver) => {
let wikiPageName = driver.wikipedia.split("/").slice(-1).toString();
let wiki_url =
"https://en.wikipedia.org/w/api.php?origin=*&action=query&titles=" +
wikiPageName +
"&prop=pageimages&format=json&pithumbsize=500";
return fetch(wiki_url);
})
);
const urls = await Promise.all(responses.map((r) => r.json())).then(
(json) => retrievingImgUrl(json)
);
setDrivers((prev) => {
return prev.map((item, idx) => {
return { ...item, image: urls[idx] };
});
});
}
const retrievingImgUrl = async (data) => {
console.log(data);
const strippingData = data.map((d) => {
return d.query.pages;
});
const urls = strippingData.map((d) => {
const k = Object.keys(d)[0];
try {
return d[k].thumbnail.source;
} catch {
return null;
}
});
return urls;
};
return (
<Fragment>
<Header getDrivers={getDrivers} />
<CardList drivers={drivers} />
</Fragment>
);
}
export default App;
Image 1 (clicked button once):
Image 2 (clicked button twice):
Object20Object error:
const Header = (props) => {
const driverYear = useRef();
const driverYearHandler = (e) => {
e.preventDefault();
console.log(driverYear);
const year = driverYear.current.value;
console.log(typeof year);
props.getDrivers(year.toString());
};
return (
<header className={classes.header}>
<Title />
<form onSubmit={driverYearHandler}>
{/* <label htmlFor="year">Enter Year:</label> */}
<input
type="text"
id="year"
ref={driverYear}
placeholder="Enter Year:"
/>
<button onClick={props.getDrivers}>Get Drivers</button>
</form>
</header>
);
};
export default Header;
Console Error:
UPDATED FETCH CALL
const getDrivers = async (year) => {
console.log("Running more than once??");
const url = "https://ergast.com/api/f1/" + year + "/drivers.json";
const response = await fetch(url);
const data = await response.json();
let driverInfo = [];
data.MRData.DriverTable.Drivers.map((driver) => {
driverInfo.push({
id: driver.code,
firstName: driver.givenName,
lastName: driver.familyName,
wikipedia: driver.url,
image: null,
});
});
getDriversImgs(driverInfo).then((data) => setDrivers(data));
console.log("Here is driver info", driverInfo);
};
const getDriversImgs = async (driverInfo) => {
const responses = await Promise.all(
driverInfo.map((driver) => {
let wikiPageName = driver.wikipedia.split("/").slice(-1).toString();
let wiki_url =
"https://en.wikipedia.org/w/api.php?origin=*&action=query&titles=" +
wikiPageName +
"&prop=pageimages&format=json&pithumbsize=500";
return fetch(wiki_url);
})
);
const urls = await Promise.all(responses.map((r) => r.json())).then(
(json) => retrievingImgUrl(json)
);
return driverInfo.map((item, idx) => {
return { ...item, image: urls[idx] };
});
};
const retrievingImgUrl = async (data) => {
const strippingData = data.map((d) => {
return d.query.pages;
});
const urls = strippingData.map((d) => {
const k = Object.keys(d)[0];
try {
return d[k].thumbnail.source;
} catch {
return null;
}
});
return urls;
};
This is likely happening because of a small misunderstanding with setState. You are calling getDriversImgs() just after setDrivers() is called, but any set state function is asynchronous. It is likely not done setting before you look for the driver's image.
The simplest solution in my opinion will be to not setDrivers until you've correlated an image to each driver. You already have all of your driverInfo in an array, so iterating through that array and finding the image for the driver should be quite straightforward.
After you've created a driverInfo array that includes the driver's image, then you can use setDrivers which will render it to the DOM.

Displaying response from API in react component

I'm trying to display the response from the API into my react component but it's not working. If I try to use it in the console, I can see the data and its value but not in the react component, it's empty when I try to show the value in a div.
Here is the code where I'm trying to display it in my react component:
const CharacterListing = () => {
const characters = useSelector(getAllCharacters);
console.log("Hello", characters);
const renderCharacters = Object.entries(characters).map(([key, value]) => {
console.log(value.name);
<div>{value.name}</div>
})
return (
<div>
{renderCharacters}
</div>
);
};
export default CharacterListing;
This is the code for my Character Slice Component
const initialState = {
characters: {},
};
const characterSlice = createSlice({
name: 'characters',
initialState,
reducers: {
addCharacters: (state, { payload }) => {
state.characters = payload;
},
},
});
export const { addCharacters } = characterSlice.actions;
export const getAllCharacters = (state) => state.characters.characters;
export default characterSlice.reducer;
This is the code for my Home Component:
const Home = () => {
const dispatch = useDispatch();
useEffect(() => {
const fetchCharacters = async () => {
const response = await baseURL.get(`/characters`)
.catch(error => {
console.log("Error", error);
});
dispatch(addCharacters(response.data));
console.log("Success", response);
};
fetchCharacters();
}, [])
return (
<div>
Home
<CharacterListing />
</div>
);
};
export default Home;
Thank you
You forgot to return item into your map func
Try this :
const renderCharacters = Object.entries(characters).map(([key, value]) => {
console.log(value.name);
return <div key={key}>{value.name}</div>
})

How to check value of useRef.current.selectionStart in jest test

I need to test the values of useRef.current.selectionStart and useRef.current.selectionEnd once they have been changed on a onKeyDown interaction with an input.
index.tsx
import React, { useRef, useEffect } from 'react'
type Props {
value: string
...aBunchOfProps
}
const SomeComponent: FC<Props> = ({ value, ...aBunchOfProps }) => {
const inputRef = useRef(null)
const [caretPosition, setCaretPosition] = useState<CaretPosition | undefined>()
useEffect(() => {
if (!!caretPosition && !!value) {
let newCaretPosition = caretPosition.currentPosition
const shouldMoveCaretForward =
caretPosition.direction === 1 && value.charAt(caretPosition.currentPosition) === '/'
if (shouldMoveCaretForward) {
newCaretPosition++
}
inputRef.current.selectionStart = newCaretPosition <=== this is the line I want to test
inputRef.current.selectionEnd = newCaretPosition <=== this is the line I want to test
}
}, [caretPosition])
const someFunction = () => {
// calls setCaretPosition with new details
}
return (
...someAdditionalCode
<input
...someAdditionalProps
ref={inputRef}
value={value}
data-testid="input-field"
onKeyDown={() => someFuction()}
/>
...evenMoreCode
)
}
export default SomeComponent
index.test.tsx
describe('SomeComponent tests', () => {
it('should move cursor correctly', () => {
const { getByTestId } = render(<SomeComonent value="12/3" />)
const input = getByTestId('input-field')
fireEvent.keyDown(input, { key: '4' })
// expect(useRef.current.selectionStart).toEqual(5) <==== I want something like this
// expect(useRef.current.selectionEnd).toEqual(5) <==== I want something like this
})
})
Any suggestions would be helpful.
I had checked your code. It May be not possible to check useRef in testcase file. please check shown below image [![enter image description here and also share that document link, so it can be helpful to you.
document link: https://testing-library.com/docs/guiding-principles/

How to use React.useMemo in custom hook react.js?

I have a custom hook for handling image inputs but I'm trying to encapsulate it with useMemo but I don't know exactly how to do this.
useImage.js
import { useState } from 'react';
export const useImage = (initialValue) => {
const [ image, setImage ] = useState(initialValue);
console.log('useImage');
return {
image,
setImage,
resetImage: () => setImage({ blob: '', file: null }),
bindImage: {
image,
onChange: (event) => {
event.persist();
const imageUrl = event.target.files.length ? URL.createObjectURL(event.target.files[0]) : '';
if (event.target && imageUrl) {
setImage((image) => ({
...image,
blob: imageUrl,
file: event.target.files[0]
}));
}
}
}
};
};

Using react to fetch data from google taxonomy api?

I'm new to react development and want to fetch data from https://www.google.com/basepages/producttype/taxonomy-with-ids.en-US.txt and post it into a tree structure.
I want to show the fetched data, not just fetch it, into a tree structure. My code is already fetching data, and I want a structure like in this image.
This is my App.js:
const proxyUrl='https://cors-anywhere.herokuapp.com/';
fetch( proxyUrl + 'https://yamzaidi.github.io/index.txt/gtaxFile.txt' )
.then(e=>e.text())
.then(req => {
var s = req.split('\n');
s.forEach(element => {
let x = element.split('>');
for(var i=0;i<x.length;i++)
{
console.log(' '+x[i]+' '+i);
}
});
})
function App() {
return (
<div>this is text</div>
);
}
export default App;
Tree structure you have to build on your own. I am not sure how u want to build a tree. Please explain further. Below is how to fetch correctly in React and update state.
Just tried for one example. See the URL https://stackblitz.com/edit/react-ie2rt6
import React, { useEffect, useState } from "react";
import "./style.css";
const App = () => {
const [text, setText] = useState();
const proxyUrl = "https://cors-anywhere.herokuapp.com/";
useEffect(() => {
fetch(proxyUrl + "https://yamzaidi.github.io/index.txt/gtaxFile.txt")
.then(e => e.text())
.then(req => {
// console.log(req);
let s = req.split('\n');
// console.log(s.slice(1,50));
const treeArray = s.slice(1,50);
console.log(treeArray[6].split('>').map((x,i) => `${'\\t'.repeat(i)} ${x}`).join('\\n'))
setText(treeArray[6].split('>').map((x,i) => `${' '.repeat(i*3)} ${x}`).join('<br/>'))
});
}, []);
const createMarkup = (text) => {
return {__html: text};
}
return (
<div>
<h1>Hello StackBlitz!</h1>
<p>Start editing to see some magic happen :)</p>
<h2 dangerouslySetInnerHTML={createMarkup(text)}></h2>
</div>
);
};
export default App;
The tree structure can be made like this
class Node {
constructor(node_data_input) {
this.data = node_data_input;
this.children = [];
}
}
class Tree {
constructor() {
this.root = new Node("Start");
}
insert(data) {
data && this.insertNode(this.root, data);
}
insertNode(node, data) {
if (!node.children.find((n) => n.data === data[0]))
node.children.push(new Node(data[0]));
else
this.insertNode(
node.children.find((n) => n.data === data[0]),
data.splice(1, data.length)
);
}
}
const tree = new Tree();
fetch("./taxonomy.txt")
.then((r) => r.text())
.then((result) => {
let data = result.split("\n").map((d) => d.split(" > "));
data.forEach((d) => tree.insert(d));
});
console.log(tree);
In this structure, any node with empty array as children is considered as leaf node.

Categories