I'm creating a React Native application. I want to create an image array because I need to upload them to the server. But when I upload multiple images they are not storing like an array. every time the imagesQueue array has only one image.
my code as follows.
const [filePath, setFilePath] = useState({
imagesQueue: []
});
const chooseFile = () => {
const options = {
title: 'Select an option',
storageOptions: {
skipBackup: true,
path: 'images',
},
};
ImagePicker.showImagePicker(options, (response) => {
// console.log('Response = ', response);
if (response.didCancel) {
console.log('User cancelled image picker');
} else if (response.error) {
console.log('ImagePicker Error: ', response.error);
} else {
// let source = response;
// You can also display the image using data:
let source = {
uri: 'data:image/jpeg;base64,' + response.data
};
setFilePath({
...filePath,
imagesQueue: [source],
});
console.log("stored item : " + filePath.imagesQueue);
}
});
};
How to store multiple images in the same state. Thanks in advance.
the data probably will store in formData - it's easy format for later passing it to BE.
firstly You need store somewhere those data:
const [selectedFiles, setSelectedFiles] = useState([]);
Later there is possibility to use .concat() - that function connects 2 arrays:
setSelectedFiles(((prevState) => prevState.concat(fileData)));
And in the last step, You need to pass those data to Your form:
const formData = new FormData();
for (let i = 0; i < selectedFiles.length; i++) {
formData.append('Files', selectedFiles[i].File);
}
Related
The senerio is that when choose categories using checkbox those checked box who are slected save their values in db in different column.
const checkListNames = check.map((item) => item.name)
values in want to save in different columns.
Getting output such as :
['A stage is required', 'Themed decoratiions are essential to my event'].
Want to save these values in different columns
const handleSubmit = (e) => {
e.preventDefault();
if (check.length > 0) {
const checkListNames = check.map((item) => item.name);
let formData = new FormData();
formData.append('list_category', categoryId);
formData.append('name', checkListNames);
formData.append('event_id', get_all_data.data7);
formData.append('created_by', '629829078779cc4a00139c9a');
for (var pair of formData.entries()) {
console.log(pair[0] + ' - ' + pair[1]);
}
api
.post('checklist', formData)
.then((response) => {
const { data } = response;
if (data.status == '1') {
toast.success('Checklist added successfully');
history.push('/CheckList');
} else {
toast.error('Something went wrong !');
}
})
.catch((error) => {
throw error;
});
} else {
toast.error('Select atleast one category');
return false;
}
};
I Am using multiselect dropdown, what i want is whatever i've selected in dropdown to send it to the server by calling an api which contains query param to accomodate these dropdown result. I have made an array of selected items.
Array(3) [ "contact", "fee", "inbox" ]
I want this array to get pass to below url like this:
http://localhost.com/api/influencers?status=contact&status=fee&status=inbox
With my approach i am ending up with this:
http://localhost:8080/details?status[]=contact&status[]=fee&status[]=inbox
Can anyone please help me with it
const InfluencersList = props => {
const [availability, setAvailability] = useState(null);
const handleAvailabilityChange = value => {
const availability1 = value;
setAvailability(value);
getFilterData(availability1, null);
};
const getFilterData = (search, pageNumber) => {
let params = {};
params.status = search; //search is array [contact,
if (pageNumber) {
params.page = pageNumber; // no is array is number
}
axios.get("/api/influencers", { params: params }).then(res => {
setState({
items: res.data.data
});
});
};
<ChoiceList
title="Availability"
titleHidden
choices={statuses}
selected={availability || []}
onChange={handleAvailabilityChange}
allowMultiple
/>
}
you would need to use URLSearchParams to build your params with multiple query with same name.
Iterate over search and for each value you append to your params a new status value:
const getFilterData = (search, pageNumber) => {
const params = new URLSearchParams();
search.forEach(value => params.append('status', value));
if (pageNumber) {
params.append('page', pageNumber) ;
}
axios.get("/api/influencers", { params }).then(res => {
setState({
items: res.data.data
});
});
};
Why not -with axios- you send a string as:
"contact;fee;inbox"
and in the backend convert this string to an array using a function like split(';')?
I'm currently trying to get a project working to test some things and I'm stuck at a point where I'm trying to update the state properly.
I have an endpoint accessed via axios.get("/docker/containers") which will return an array for all IDs of the containers which are currently running on my system this is done like so:
componentDidMount() {
this.interval = setInterval(() => this.updateContainers(), 3000);
};
componentWillUnmount() {
clearInterval(this.interval);
}
At this point my state looks like this:
state = {
containers: [{id: 'id1'}, {id: 'id2'}]
}
The user interface then just shows a list of IDs.
I can then click on an ID on my user interface and it will set a watcher:
state = {
containers: [{id: 'id1', watcher: true}, {id: 'id2'}]
}
The point of the watcher is so that on the next update cycle more detailed information about a particular container is retrieved.
state = {
containers: [{id: 'id1', watcher: true, name: 'container1'}, {id: 'id2'}]
}
Upon clicking the container in the user interface where a watcher is already set then the watcher is dropped and the more detailed information is then no longer retrieved
state = {
containers: [{id: 'id1', watcher: false}, {id: 'id2'}]
}
Where I'm getting stuck is on how to get the more detailed information. My updateContainers method has 3 steps:
Read the response from the API and destruct the state into separate variables, compare the state var with the response var and remove any containers that have gone down (no setState is done here).
Add any new containers from the response to the state that have since come up (again no setState).
...All good thus far...
Loop through the filtered array of containers from steps 1 and 2 and find any containers where a watcher is set. Where it is set perform an API call to retrieve the more detailed info. Finally set the state.
In step 3 I use a forEach on the filtered array and then do an axios.get("/docker/containers/id1") where a watcher has been set otherwise simply keep the container details I already have but that's where I get stuck, Typescript is also giving me the error:
TS2322: Type 'void' is not assignable to type 'IndividualContainer[]'.
currently I have:
updateContainers() {
axios.get('/docker/containers')
.then(response => {
const apiRequestedContainers: string[] = response.data.containers;
// array of only IDs
const stateContainers: IndividualContainer[] = [
...this.state.containers
];
// remove dead containers from state by copying still live containers
let filteredContainers: IndividualContainer[] = [
...this.filterOutContainers(stateContainers, apiRequestedContainers)
];
// add new containers
filteredContainers = this.addContainerToArray(
filteredContainers, apiRequestedContainers
);
return this.updateContainer(filteredContainers);
})
.then(finalArray => {
const newState: CState = {'containers': finalArray};
this.setState(newState);
});
};
updateContainer(containers: IndividualContainer[]) {
const returnArray: IndividualContainer[] = [];
containers.forEach(container => {
if (container.watcher) {
axios.get('/docker/containers/' + container.id)
.then(response => {
// read currently available array of containers into an array
const resp = response.data;
resp['id'] = container.id;
resp['watcher'] = true;
returnArray.push(resp);
});
} else {
returnArray.push(container);
}
return returnArray;
});
};
Any pointers to where my logic fails would be appreciated!
Edit:
Render Method:
render() {
const containers: any = [];
const curStateOfContainers: IndividualContainer[] = [...this.state.containers];
if (curStateOfContainers.length > 0) {
curStateOfContainers.map(container => {
const container_id = container.id.slice(0, 12);
containers.push(
<Container
key = {container_id}
container_id = {container.id}
name = {container.name}
clickHandler = {() => this.setWatcher(container.id)}
/>
);
});
}
return containers;
}
I'm not an expert in TypeScript so I had to change the response to JS and thought you'll re-write it in TS in case it's needed.
async updateContainers() {
const response = await axios.get('/docker/containers')
const apiRequestedContainers = response.data.containers; // array of only IDs
const stateContainers = [...this.state.containers];
// remove dead containers from state by copying still live containers
let filteredContainers = [...this.filterOutContainers(stateContainers, apiRequestedContainers)];
// add new containers
filteredContainers = this.addContainerToArray(filteredContainers, apiRequestedContainers);
const containers = await this.updateContainer(filteredContainers)
this.setState({ containers });
};
async updateContainer(containers) {
return containers.map(async (container) => {
if (container.watcher) {
const response = await axios.get('/docker/containers/' + container.id)
// read currently available array of containers into an array
return {
...response.data,
id: container.id,
watcher: true,
}
} else {
return container;
}
});
}
Here's what I've updated in updateContainer:
I'm now mapping the array instead of doing a forEach
I'm now waiting for the container details API to return a value before checking the second container. --> this was the main issue as your code doesn't wait for the API to finish ( await / async )
The problem is that you are returning nothing from updateContainer method which will return void implicitly:
// This function return void
updateContainer(containers: IndividualContainer[]) {
const returnArray: IndividualContainer[] = [];
containers.forEach(container => {
if (container.watcher) {
axios.get("/docker/containers/" + container.id).then(response => {
// read currently available array of containers into an array
const resp = response.data;
resp["id"] = container.id;
resp["watcher"] = true;
returnArray.push(resp);
});
} else {
returnArray.push(container);
}
// this is inside the forEach callback function not updateContainer function
return returnArray;
});
}
Then you assign void to containers which is supposed to be of type IndividualContainer[] so TypeScript gives you an error then you set that in the state:
updateContainers() {
axios
.get("/docker/containers")
.then(response => {
const apiRequestedContainers: string[] = response.data.containers; // array of only IDs
const stateContainers: IndividualContainer[] = [
...this.state.containers
];
// remove dead containers from state by copying still live containers
let filteredContainers: IndividualContainer[] = [
...this.filterOutContainers(stateContainers, apiRequestedContainers)
];
// add new containers
filteredContainers = this.addContainerToArray(
filteredContainers,
apiRequestedContainers
);
// this return void as well
return this.updateContainer(filteredContainers);
})
// finalArray is void
.then(finalArray => {
// you assign void to containers which should be of type IndividualContainer[]
const newState: CState = { containers: finalArray };
// containers will be set to undefined in you state
this.setState(newState);
});
}
You meant to do this:
// I added a return type here so that TypeScript would yell at me if I return void or wrong type
updateContainer(containers: IndividualContainer[]): IndividualContainer[] {
const returnArray: IndividualContainer[] = [];
containers.forEach(container => {
if (container.watcher) {
axios.get("/docker/containers/" + container.id).then(response => {
// read currently available array of containers into an array
const resp = response.data;
resp["id"] = container.id;
resp["watcher"] = true;
returnArray.push(resp);
});
} else {
returnArray.push(container);
}
// removed the return from here as it's useless
});
// you should return the array here
return returnArray;
}
First, I've commented on errors in your code:
updateContainers() {
axios.get('/docker/containers')
.then(response => {
...
return this.updateContainer(filteredContainers);
// returns `undefined`...
})
.then(finalArray => { ... });
// ...so `finalArray` is `undefined` - the reason for TS error
// Also `undefined` is not a `Promise` so this second `then()`
// doesn't make much sense
};
updateContainer(containers: IndividualContainer[]) {
const returnArray: IndividualContainer[] = [];
containers.forEach(container => {
if (container.watcher) {
axios.get('/docker/containers/' + container.id)
.then(response => {
...
returnArray.push(resp)
// because `axios.get()` is asynchronous
// this happens only some time after
// `.then(finalArray => { ... })` is finished
});
// at this moment code inside `.then()` has not been executed yet
// and `resp` has not yet been added to `returnArray`
} else {
returnArray.push(container)
// but this happens while `forEach()` is running
}
return returnArray;
// here you return from `forEach()` not from `updateContainer()`
// also `forEach()` always returns `undefined`
// so even `return containers.forEach(...)` won't work
});
// no return statement, that implicitly means `return undefined`
};
Now, why the #RocKhalil's answer, kind of, works:
async updateContainers() {
const response = await axios.get('/docker/containers')
// he favors a much clearer syntax of async/await
...
const containers = await this.updateContainer(filteredContainers)
this.setState({ containers });
};
async updateContainer(containers) {
return containers.map(async (container) => {
if (container.watcher) {
const response = await axios.get('/docker/containers/' + container.id)
// Because `axios.get()` was **awaited**,
// you can be sure that all code after this line
// executed when the request ended
// while this
// axios.get(...).then(() => console.log(2)); console.log(1)
// will lead to output 1 2, not 2 1
return {
...response.data,
id: container.id,
watcher: true,
}
} else {
return container;
}
});
// he does not forget to return the result of `map()`
// and `map()` in contrast with `forEach()` does have a result
// But...
}
But...
containers.map() returns an array. An array of Promises. Not a single Promise. And that means that
const containers = await this.updateContainer(filteredContainers)
waits for nothing. And updateContainer() function is not actually async.
To fix that you need to use Promise.all():
const containers = await Promise.all(this.updateContainer(filteredContainers))
I'm trying to fetching my data from mongo to the client-side to react.
I succeed to set the state of the component to the correct fields from the endpoint API of my DB.
But when I want to print my state to see if it is working the console print my an empty object although the state change and I see it in the console.[enter image description here][1]
getDataFromDb = () => {
const req = new Request('http://localhost:5000/family',{
method: 'GET',
cache: 'default'
});
fetch(req).then(res=>{
return res.json();
}).then(data=>{
console.log(data);
this.setState({
rooms: data
});
console.log(this.state.rooms);
}).
catch(err=>{
console("Error: " + err);
});
};
componentDidMount() {
this.getDataFromDb().then(result => this.setState({rooms: result}));
//let rooms = this.formatData(this.getDataFromDb());
//let featuredRooms = ...rooms.filter(room => room.featured===true);
//let maxPrice = Math.max(...rooms.map(item=>item.price));
//let maxSize = Math.max(...rooms.map(item=>item.size));
//new code:
let featuredRooms = this.state.rooms.filter(room=>room.featured===true);
let maxPrice = Math.max(this.state.rooms.map(item => item.price));
let maxSize = Math.max(this.state.rooms.map(item=> item.size));
this.setState({
// old code ---> rooms,
//rooms,
featuredRooms,
sortRooms: this.state.rooms,
//old code
//sortedRooms:rooms,
loading:false,
price:maxPrice,
maxPrice,
maxSize
});
this.printData();
}
In react, setState is an async function. Printing the state (just after changing it) will not give you the latest state. If you want to trigger a function after setting the state you can do the following:
this.setState({
// old code ---> rooms,
//rooms,
featuredRooms,
sortRooms: this.state.rooms,
//old code
//sortedRooms:rooms,
loading:false,
price:maxPrice,
maxPrice,
maxSize
}, () => { this.printData(); });
I'm in the process of sending multiple files from "React.js" by formData.append() to a backend.
At the backend(Spring boot), I was able to see that multiple files were saved well with postman.
The problem occurred in React.
(I'm using "Ant Design" that is React UI Library.)
Below is the source that append files to formdata with extra data.
const formData = new FormData();
formData.append('webtoonId', this.state.selectedToonId);
formData.append('epiTitle', this.state.epiTitle);
formData.append('eFile', this.state.thumbnail[0].originFileObj);
for( let i = 0; i< this.state.mains.length ; i++){
formData.append('mFiles', this.state.mains[i].originFileObj);
}
uploadEpi(formData)
uploadEpi() is POST API.
Below is about state.
this.state = {
toons: [],
epiTitle :'',
thumbnail : [],
mains : [],
selectedToonID : ''
}
When I submit, Text and single file are stored in the DB normally, but only multiple files cannot be saved.
There was no error. Just multiple files didn't be saved.
The state "mains" is configured as shown below.
I guess it's because I'm using Ant Design incorrectly.
(Ant Design : https://ant.design/components/upload/)
Why I guessed so, because when I add multiple attribute to <Dragger> like below,
<Dragger onChange={this.onChangeMain} beforeUpload={() => false} multiple={true}>
the state "mains" multiple files became undefined.
Below is onChange={this.onChangeMain}
onChangeMain=({ fileList })=> {
this.setState({ mains : fileList }, function(){
console.log(this.state)
});
}
The bottom line is, I want to know how to upload multiple files through <Upload> (or <Dragger>) in "React Ant Design."
I don't know what should I do.
this is my github about this project.
I'd appreciate with your help. thx.
const [loading, setLoading] = useState<boolean>(false);
const [fileList, setFileList] = useState<any[]>([]);
const [/* fileListBase64 */, setFileListBase64] = useState<any[]>([]);
const propsUpload = {
onRemove: (file:any) => {
const index = fileList.indexOf(file);
const newFileList:any = fileList.slice();
newFileList.splice(index, 1);
return setFileList(newFileList)
},
beforeUpload: (file:any) => {
setFileList([...fileList, file]);
return false;
},
onChange(info:any) {
setLoading(true);
const listFiles = info.fileList;
setFileList(listFiles);
const newArrayFiles = listFiles.map((file:any) => file.originFileObj? (file.originFileObj) : file );
const anAsyncFunction = async (item:any) => {
return convertBase64(item)
}
const getData = async () => {
return Promise.all(newArrayFiles.map((item:any) => anAsyncFunction(item)))
}
getData().then(data => {
/* setFileSend(data) */
setFileListBase64(data);
setLoading(false);
// console.log(data);
});
},
directory: true,
fileList: fileList,
};
const convertBase64 = (file:File) => {
return new Promise((resolve, reject) => {
const fileReader = new FileReader();
fileReader.readAsDataURL(file)
fileReader.onload = () => {
resolve(fileReader?.result);
}
fileReader.onerror = (error) => {
reject(error);
}
})
}
const handleDeleteListFiles = () => {
setFileList([]);
setFileListBase64([]);
}
It seems like you are overriding the value of mFiles.
const formData = new FormData();
formData.append('webtoonId', this.state.selectedToonId);
formData.append('epiTitle', this.state.epiTitle);
formData.append('eFile', this.state.thumbnail[0].originFileObj);
let mFiles = [];
for( let i = 0; i< this.state.mains.length ; i++){
mFiles[i] = this.state.mains[i].originFileObj;
}
formData.append('mFiles', mFiles)
uploadEpi(formData)
Maybe this can work: formData.append('mFiles[]', mFiles)
If you add [] to the string it should not overwrite but add to the array