How to re-render screen when updating array? - javascript

Using the image-crop-picker I am selecting an image from gallery then setting it to state. I then have the option to crop the image and in the array/state I replace the old image with the new cropped one which I am able to do so successfully but the screen doesn't update with the cropped image until I refresh it.
import ImagePicker from 'react-native-image-crop-picker';
const [renderImages, setRenderImages] = useState([]);
//Listens for images
useEffect(() => {
renderImages;
}, [renderImages]);
//Pick images from gallery
const pickGalleryImages = () => {
let imageList = [];
ImagePicker.openPicker({
multiple: true,
mediaType: 'any',
maxFiles: 10,
cropping: true,
})
.then(response => {
response.map(imgs => {
imageList.push(imgs.path);
});
setRenderImages(imageList);
})
.catch(() => null);
};
//Crop image
const cropImage = item => {
ImagePicker.openCropper({
path: item.imgs,
width: 400,
height: 400,
})
.then(image => {
const oldImage = renderImages.findIndex(img => img.imgs === item.imgs);
renderImages[oldImage] = {imgs: image.path};
})
.catch(() => null);
};

It seems that cropImage is not doing setRenderImages to update the state, therefore the component is not re-rendered.
Try do a setRenderImages in cropImage:
UPDATE
For keeping existing places of images in array:
//Crop image
const cropImage = (item) => {
ImagePicker.openCropper({
path: item.imgs,
width: 400,
height: 400,
})
.then((image) => {
setRenderImages((prev) => {
const oldIndex = prev.findIndex((img) => img.imgs === item.imgs);
const newImages = [...prev];
newImages[oldIndex] = { imgs: image.path };
return newImages;
});
})
.catch(() => null);
};
Original
For pushing latest cropped image to start of array:
//Crop image
const cropImage = (item) => {
ImagePicker.openCropper({
path: item.imgs,
width: 400,
height: 400,
})
.then((image) => {
setRenderImages((prev) => {
const oldImages = prev.filter((img) => img.imgs !== item.imgs);
return [{ imgs: image.path }, ...oldImages];
});
})
.catch(() => null);
};
Hope this will help!

you should use setRnderImages for update state:
//Crop image
const cropImage = item => {
ImagePicker.openCropper({
path: item.imgs,
width: 400,
height: 400,
})
.then(image => {
const oldImage = renderImages.findIndex(img => img.imgs === item.imgs);
let newImages= [...renderImages]
newImages[oldImage] = {imgs: image.path};
setRnderImages(newImages)
})
.catch(() => null);
};

Related

React: how can I get multiple image size?

I have an object with links
const urls = {
small: https://image1.com,
medium: https://image2.com,
large: https://image3.com,
};
output should look like that:
{
small: {width: image_width, height: image_height},
medium: {width: image_width, height: image_height},
large: {width: image_width, height: image_height}
}
I tried like this, but it turns out an infinite loop.
Help me fix my function or show how it is possible in another way
const [imageSize, setImageSize] = useState({});
const getImageSize = () => {
for (const url in urls) {
const img = new Image();
img.src = urls[url];
img.onload = () => {
setImageSize((prev) => {
return {
...prev,
[url]: {
width: img.width,
height: img.height,
},
};
});
};
}
};
To trigger image onload listener it should be mounted to the DOM, otherwise your setImageSize not called. To do this you should have valid images and attach it somewhere to the html, like this:
const urls = {
small: 'https://dummyimage.com/600x400/000/fff',
medium: 'https://dummyimage.com/600x400/000/fff',
large: 'https://dummyimage.com/600x400/000/fff',
}
const getImageSize = () => {
for (const url in urls) {
const img = new Image()
img.src = urls[url]
img.onload = () => {
setImageSize(prev => {
return {
...prev,
[url]: {
width: img.width,
height: img.height,
},
}
})
}
document.body.appendChild(img)
}
}
useEffect(() => {
getImageSize()
}, [])
Try this below code:
const [imageSize, setImageSize] = useState({});
const urls = {
small: https://image1.com,
medium: https://image2.com,
large: https://image3.com,
};
const getImageSize = () => {
for (const size in urls) {
var img = new Image();
img.onload = function() {
setImageSize((prev) => {
return {
...prev,
[size]: {width: this.width, height: this.height}
}
})
};
img.src = url;
}
}
useEffect(() => {
getImageSize()
}, [])
check the fiddle link I hope it will work[https://jsfiddle.net/k7bue0ho/][1]

how to run useEffect only twice

Here is My useEffect is going in Infinite loop, becouse checkimage value is changing becouse the value is assigned in fetch(), so anyone know how to solve it. I want to get varient data with image but I can't get it in first time.
help me if you can
Thank You
useEffect(() => {
fetch({ pagination });
}, [checkimage]);
const fetch = async (params = {}) => {
if (type == 'product') {
dispatch(await ProductService.getProduct(productId))
.then((res) => {
let variantsdatas = getImageArns(res.data.variants);
getImages(variantsdatas);
let record = [];
record.push(res.data)
setVarientsData(record)
})
.catch((err) => {});
} else {
dispatch(await ProductService.getProducts())
.then((res) => {
console.info({ 'res.data': res.data });
setVarientsData(res.data.products);
setPagination({
...params.pagination,
total: res.total_count,
});
})
.catch((err) => {});
}
};
const getImageArns = (variantsdatas) => {
const variantImageArns = [];
variantsdatas.forEach((variant, index) => {
variant[index] = variant.variantId;
if (variant.variantImagesListResponseDto.images.length > 0) {
let variantImageObj = {
variantId: variant.variantId,
arnUrl: variant.variantImagesListResponseDto.images[0].docUrl,
};
variantImageArns.push(variantImageObj);
}
});
// console.info('id', variantImageArns);
return variantImageArns;
};
const getImages = async (variantsdatas) => {
const images = [];
dispatch(await ProductVariantService.getImage(variantsdatas))
.then((res) => {
console.info(res.data.fileResponseDtoList);
let presignedURLs = {};
res.data.fileResponseDtoList.map(
(i) => (
(presignedURLs = {
variantId: i.variantId,
arnUrl: i.presignedURL,
}),
console.info(presignedURLs),
images.push(presignedURLs)
)
);
setcheckimage(images);
})
.catch((err) => {
console.info('Get Error District...');
});
};
var img = 'img';
const setVarientsData = (products) => {
let varients_array = [];
if (products.length > 0) {
products.forEach((product) => {
if (product.variants.length > 0) {
let product_varients = product.variants;
product_varients.forEach((varient) => {
for (var f = 0; f < checkimage.length; f++) {
if(checkimage[f].variantId == varient.variantId){
img = checkimage[f].arnUrl;
f = checkimage.length
}
else{
img = 'img2';
}
}
varients_array.push({
image: img,
variantId: varient.variantId,
productVariantName: varient.variantName,
productName: product.productName,
brand: '-',
sellerSku: varient.productVariantCode,
status: product.status,
category: product.subCategoryInfo.categoryInfo.categoryName,
subCategoryName: product.subCategoryInfo.subCategoryName,
state: '-',
market: '-',
mrp: varient.price.amount + ' ' + varient.price.currency,
sellingPrice: '-',
manufacturer_product_variant_code:
varient.manufacturerProductVariantCode,
product_varient_code: varient.azProductVariantLongCode,
hsnCode: varient.hsnCode,
});
});
}
});
}
setVarients(varients_array);
console.info('varients_array ====>>', {varients_array})
};
I think that if I stop to run blow code getImage function then I can get my result
am I right?
But I tried It too but is also not happening properly.
a quick and dirty fix could be to work with a counter.
and only run the fetch in the useEffect, when counter is 0.
have you tried that?

Code is moving on before my fetch is completed

I'm having a problem where inside my useEffect whenever I call getRoomDetails, the code continues on before it's finished fetching data from my API. So it will create the Chess object with the default value I've given for boardState, instead of the updated value from my API. How could I get it so it waits until getRoomDetails finishes, before moving onto creating the Chess object.
const initialState = {
hostTime: 600,
guestTime: 600,
chessAnnotations: "",
isHost: true,
fen: "start",
}
const getRoomDetails = () => {
fetch('/api/get-room?code=' + roomCode).then((response) =>
response.json()
).then((data) => {
const newObj = {
hostTime: data.host_curr_time,
guestTime: data.guest_curr_time,
chessAnnotations: data.chess_annotations,
isHost: data.is_host,
fen: data.fen,
};
setBoardState(newObj);
console.log(newObj)
});
}
const [boardState, setBoardState] = useState(initialState);
let game = useRef(null);
useEffect(() => {
getRoomDetails();
console.log(boardState.fen + "lit");
game.current = new Chess(boardState.fen);
console.log("0");
}, []);
Output:
start 0
0
Object { hostTime: "600.00", guestTime: "600.00", chessAnnotations: "sdf", isHost: false, fen: "rnbqkbnr/pppppppp/8/8/8/3P4/PPP1PPPP/RNBQKBNR b KQkq - 0 1" }
See the explanation in the inline comments
const initialState = {
hostTime: 600,
guestTime: 600,
chessAnnotations: "",
isHost: true,
fen: "start",
}
const getRoomDetails = () => {
// HERE: Return the promise
return fetch('/api/get-room?code=' + roomCode).then((response) =>
response.json()
).then((data) => {
const newObj = {
hostTime: data.host_curr_time,
guestTime: data.guest_curr_time,
chessAnnotations: data.chess_annotations,
isHost: data.is_host,
fen: data.fen,
};
setBoardState(newObj);
console.log(newObj)
});
}
const [boardState, setBoardState] = useState(initialState);
let game = useRef(null);
useEffect(() => {
getRoomDetails()
// HERE: run this block after the promise is resolved
.then(() => {
console.log(boardState.fen + "lit");
game.current = new Chess(boardState.fen);
console.log("0");
});
}, []);

undefined after setState() (use hook) in Reactjs

I learn react and js myself. please explain why this situation occurs. PS: excuse me for the large text, I tried to explain the problem as clearly as possible. thanks. Essence of the matter: set the initial state through the hook:
const [pokemon, setPokemon] = useState({
img: "",
name: "",
types: [],
abilities: [],
moveList: [],
weight: "",
height: "",
description: "",
genus: "",
chanceToCatch: "",
evolutionURL: ""
});
further I make api requests to get information from inside useEffect:
useEffect(() => {
const fetchData = async () => {
await Axios({
method: "GET",
url: urlPokemonAPI
})
.then(result => {
const pokemonResponse = result.data;
/* Pokemon Information */
const img = pokemonResponse.sprites.front_default;
const name = pokemonResponse.name;
const weight = Math.round(pokemonResponse.weight / 10);
const height = pokemonResponse.height / 10;
const types = pokemonResponse.types.map(type => type.type.name);
const abilities = pokemonResponse.abilities.map(
ability => ability.ability.name
);
const moveList = pokemonResponse.moves.map(move => move.move.name);
setPokemon(() => {
return {
img: img,
name: name,
weight: weight,
types: types,
abilities: abilities,
moveList: moveList,
height: height
};
});
})
await Axios({
method: "GET",
url: urlPokemonSpecies
}).then(result => {
let description = "";
result.data.flavor_text_entries.forEach(flavor => {
if (flavor.language.name === "en") {
description = flavor.flavor_text;
}
});
let genus = "";
result.data.genera.forEach(genera => {
if (genera.language.name === "en") {
genus = genera.genus;
}
});
const evolutionURL = result.data.evolution_chain.url;
const eggGroups = result.data.egg_groups.map(
egg_group => egg_group.name
);
const chanceToCatch = Math.round(
(result.data.capture_rate * 100) / 255
);
setPokemon(pokemon => {
return {
...pokemon,
description: description,
genus: genus,
chanceToCatch: chanceToCatch,
evolutionURL: evolutionURL,
eggGroups: eggGroups
};
});
});
};
fetchData();
}, [urlPokemonAPI, urlPokemonSpecies]);
The problem arises specifically with eggGroups (with identical handling of abilities and types there is no such problem). And this is what happens when I want to output data to a page as <div> Egg Group: {pokemon.eggGroups} </div> the data is displayed normally, but as soon as I want to output eggGroups as well as abilities and types separated by commas (join ( ',')) - error: TypeError: pokemon.eggGroups is undefined. I decided to check this matter through the console and stuffed this eggGroups key into the timeout:
At some point, eggGroups becomes undefined ... why, I can’t understand. But if I set the state separately, like const [egg, setEgg] = useState ([]); setEgg (eggGroups); such a problem is not observed. why is this happening? everything was fine with types and abilities. Thank you in advance.
state updater from hooks doesn't merge the state values when updating state, instead it just replaces the old value with new one
Since you use state updater like
setPokemon(() => {
return {
img: img,
name: name,
weight: weight,
types: types,
abilities: abilities,
moveList: moveList,
height: height
};
});
eggGroups property is lost and hence it becomes undefined. You need to update it by spreading the previous state values obtained from callback
setPokemon((prev) => {
return {
...prev
img: img,
name: name,
weight: weight,
types: types,
abilities: abilities,
moveList: moveList,
height: height
};
});
Your code have a problem, this is the proper way to do await with axios,
you need to import axios like this
import axios from 'axios';
the await should be call with a promise, then it return the data from api like this:
const result = await axios.get(urlPokemonAPI);
This is the code snippet with the same logic to your code
useEffect(() => {
const fetchData = async () => {
// import axios from 'axios';
try {
const result = await axios.get(urlPokemonAPI);
const pokemon = result.data;
setPokemon({
img: pokemon.sprites.front_default,
name: pokemon.name,
weight: Math.round(pokemon.weight / 10),
types: pokemon.types.map(i => i.type.name),
abilities: pokemon.abilities.map(i => i.ability.name),
moveList: pokemon.moves.map(i => i.move.name),
height: pokemon.height / 10
});
const result2 = await axios.get(urlPokemonSpecies);
const data = result2.data;
let description = "";
data.flavor_text_entries.forEach(i => {
const lang = i.language.name
if (lang === "en") {
description = i.flavor_text;
}
});
let genus = "";
data.genera.forEach(i => {
const lang = i.language.name;
if (lang === "en") {
genus = i.genus;
}
});
setPokemon(pokemon => {
return {
...pokemon,
description,
genus,
chanceToCatch: Math.round((data.capture_rate * 100) / 255),
evolutionURL,
eggGroups: data.egg_groups.map(g => g.name)
};
});
} catch (e) {
console.log(e);
}
};
fetchData();
}, [urlPokemonAPI, urlPokemonSpecies]);
do you see another problem: you call setPokemon two times, let's rewrite it again:
useEffect(() => {
const fetchData = async () => {
// import axios from 'axios';
try {
const result = await axios.get(urlPokemonAPI);
const data1 = result.data;
const result2 = await axios.get(urlPokemonSpecies);
const data2 = result2.data;
function resolveDescription(data) {
let description = "";
data.flavor_text_entries.forEach(i => {
const lang = i.language.name
if (lang === "en") {
description = i.flavor_text;
}
});
return description;
}
function resolveGenus(data) {
let genus = "";
data.genera.forEach(i => {
const lang = i.language.name;
if (lang === "en") {
genus = i.genus;
}
});
return genus;
}
setPokemon({
img: data1.sprites.front_default,
name: data1.name,
weight: Math.round(data1.weight / 10),
types: data1.types.map(i => i.type.name),
abilities: data1.abilities.map(i => i.ability.name),
moveList: data1.moves.map(i => i.move.name),
height: data1.height / 10,
description: resolveDescription(data2),
genus: resolveGenus(data2),
chanceToCatch: Math.round((data2.capture_rate * 100) / 255),
evolutionURL: data2.evolution_chain.url,
eggGroups: data2.egg_groups.map(g => g.name)
});
} catch (e) {
console.log(e);
}
};
fetchData();
}, [urlPokemonAPI, urlPokemonSpecies]);

How to use useState and then execute a function with the newest state in React?

I'm using useState to manage the state of my component. I need to create the state and then execute a function to call my API with the state of the component.
I tried doing like this:
const [uploadedFile, setUploadedFile] = useState(null);
const handleUpload = file => {
const upload = {
file: file[0],
name: file[0].name,
preview: URL.createObjectURL(file[0]),
progress: 0,
uploaded: false,
error: false,
url: null
};
setUploadedFile(upload);
processUpload(file[0]);
};
const processUpload = file => {
const data = new FormData();
data.append("file", file, file.name);
api
.put("/Private/user/documentupload", data, {
onUploadProgress: e => {
const progress = parseInt(Math.round((e.loaded * 100) / e.total));
setUploadedFile({ ...uploadedFile, progress });
}
})
.then(response => {
console.log(response);
setUploadedFile({
...uploadedFile,
uploaded: true,
url: response.data.identity.url
});
})
.catch(error => {
setUploadedFile({ ...uploadedFile, error: true });
});
};
However, when processUpload(file[0]) is called, the state of the component is null (the initial state). I tried using useEffect:
const [uploadedFile, setUploadedFile] = useState(null);
const handleUpload = file => {
const upload = {
file: file[0],
name: file[0].name,
preview: URL.createObjectURL(file[0]),
progress: 0,
uploaded: false,
error: false,
url: null
};
setUploadedFile(upload);
};
useEffect(() => {
processUpload(uploadedFile.file);
}, [uploadedFile]);
const processUpload = file => {
const data = new FormData();
data.append("file", file, file.name);
api
.put("/Private/user/documentupload", data, {
onUploadProgress: e => {
const progress = parseInt(Math.round((e.loaded * 100) / e.total));
setUploadedFile({ ...uploadedFile, progress });
}
})
.then(response => {
console.log(response);
setUploadedFile({
...uploadedFile,
uploaded: true,
url: response.data.identity.url
});
})
.catch(error => {
setUploadedFile({ ...uploadedFile, error: true });
});
};
But this doesn't work, because uploadedFile is null when the component first render. How should I handle this?
Thanks in advance
edit
Start your upload from handleUpload:
const handleUpload = file => {
const upload = {
file: file[0],
name: file[0].name,
preview: URL.createObjectURL(file[0]),
progress: 0,
uploaded: false,
error: false,
url: null
};
setUploadedFile(upload);
processUpload(file[0]);
};
Or check on upload.file instead of just upload:
useEffect(() => {
if (uploadedFile) {
processUpload(uploadedFile.file);
}
}, [uploadedFile && uploadedFile.file]);
In this case it will only re-trigger if the file actualy changed.
But I would probably just call the upload from the handler.
Old answer:
Add a check in your useEffect:
useEffect(() => {
if (uploadedFile) {
processUpload(uploadedFile.file);
}
}, [uploadedFile]);

Categories

Resources