Why this function doesn't rendering in react - reactjs

first, Thank you for entering like here
i want to be able to use like this code, this code is rendering in react component but second code doesn't work ..
That's what i worder sir.
function forFourMultiplyFour(_pictures) {
if (_pictures.length === 0) {
return '';
}
return <div
style={{ display: 'flex', justifyContent: 'center' }}>{_pictures.map((el) => {
return <div style={{ margin: '5px' }}>
<Img key={el.id} src={el.src} alt="picture"></Img>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<div>{el.title}</div>
<div>생성일자</div>
</div>
</div>;
})}</div>;
}
function makeHowManyPage(count) {
// 태그안에 함수로 또 다른 태그를 감싼다음에 forFourMultiplyFour로 한 것처럼 렌더링할 것을 return 했는데
// 안되서 state을 배열로 만들어서
return <div
className="makeHowManyPage"
style={{ display: 'flex', justifyContent: 'center' }}>
{() => {
for (let i = 1; i <= count; i++) {
return <div>{i}</div>
}
}}
</div>
}
and then i do render like this
import React, { useState, useEffect } from 'react';
import styled from 'styled-components';
import dummyPictures from '../../../dummyDate';
function Gallery() {
const [forRenderingOne, setForRenderingOne] = useState(<div></div>);
const [forRenderingTwo, setForRenderingTwo] = useState(<div></div>);
const [forRenderingThree, setForRenderingThree] = useState(<div></div>);
const [forRenderingFour, setForRenderingFour] = useState(<div></div>);
const [pageCount, setPageCount] = useState(<div>1</div>);
const [_temp, set_Temp] = useState(['안녕하세요', '안녕하세요', '안녕하세요', '안녕하세요'])
// 애초에 4개씩 받아서 뿌릴 것
function forFourMultiplyFour(_pictures) {
if (_pictures.length === 0) {
return '';
}
return <div
style={{ display: 'flex', justifyContent: 'center' }}>{_pictures.map((el) => {
return <div style={{ margin: '5px' }}>
<Img key={el.id} src={el.src} alt="picture"></Img>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<div>{el.title}</div>
<div>생성일자</div>
</div>
</div>;
})}</div>;
}
function makeHowManyPage(count) {
// 태그안에 함수로 또 다른 태그를 감싼다음에 forFourMultiplyFour로 한 것처럼 렌더링할 것을 return 했는데
// 안되서 state을 배열로 만들어서
return <div
className="makeHowManyPage"
style={{ display: 'flex', justifyContent: 'center' }}>
{() => {
for (let i = 1; i <= count; i++) {
return <div>{i}</div>
}
}}
</div>
}
useEffect(() => {
// 서버에서 줄때 무조건 객체 16개가 든 배열이 응답해와야 정상작동되는 코드다..
setPageCount(makeHowManyPage(5))
setForRenderingOne(forFourMultiplyFour(dummyPictures.pictures.slice(0, 4)));
setForRenderingTwo(forFourMultiplyFour(dummyPictures.pictures.slice(4, 8)));
setForRenderingThree(forFourMultiplyFour(dummyPictures.pictures.slice(8, 12)));
setForRenderingFour(forFourMultiplyFour(dummyPictures.pictures.slice(12)));
}, []);
return (
<div>
{/* {forRenderingOne}
{forRenderingTwo}
{forRenderingThree}
{forRenderingFour} */}
{()=>{ return <div>'안녕하세요'</div>}}
</div>
)
}
export default Gallery
const Img = styled.img`
width: 15vw;
height: 20vh;
`

As stated this code snippet not working,
function makeHowManyPage(count) {
// 태그안에 함수로 또 다른 태그를 감싼다음에 forFourMultiplyFour로 한 것처럼 렌더링할 것을 return 했는데
// 안되서 state을 배열로 만들어서
return <div
className="makeHowManyPage"
style={{ display: 'flex', justifyContent: 'center' }}>
{() => {
for (let i = 1; i <= count; i++) {
return <div>{i}</div>
}
}}
</div>
}
Have a look at this snippet returning div not array,
for (let i = 1; i <= count; i++) {
return <div>{i}</div>
}
map works differently.
Consider changing this to,
function makeHowManyPage(count) {
// 태그안에 함수로 또 다른 태그를 감싼다음에 forFourMultiplyFour로 한 것처럼 렌더링할 것을 return 했는데
// 안되서 state을 배열로 만들어서
let array = [];
for (let i = 1; i <= count; i++) {
array.push(<div key={i}>{i}</div>);
}
return (
<div
className="makeHowManyPage"
style={{ display: "flex", justifyContent: "center" }}
>
{array}
</div>
);
}
Then usage,
export default function App() {
// declaration
let elements = makeHowManyPage(5);
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
{elements}
</div>
);
}

I don't understand what you exactly wanted, I just write some code that I expected to solve your problem.
I hope it can help your problem though it is not the best solution.
First, I divide Gallery component into two components including your forFourMultiplyFour function.
(I don't have dummy pictures, so I used a picture list api for this.)
Second, as windowsill's comment, write simple value for the useState initial value. I think you only need two values, pages and images array.
Usually people get the image list changing page and offset value not slicing image array. (If I have a wrong idea, please let me know.)
So I just change page state when clicking button.
For 4*4 image arrangement, I used flex property.
import React, { useState, useEffect } from "react";
import axios from "axios";
import ForFourMultiplyFour from "./ForFourMultiplyFour";
function Gallery() {
const [page, setPage] = useState(1);
const [images, setImages] = useState([]);
const URL = "https://picsum.photos/v2/list";
const LIMIT = 16;
useEffect(() => {
async function fetchImages() {
const { data } = await axios.get(`${URL}?page=${page}&limit=${LIMIT}`);
setImages(data);
}
fetchImages();
}, [images, page]);
useEffect(() => {
setPage(page)
}, [page])
return <ForFourMultiplyFour images={images} page={page} setPage={setPage} />;
}
export default Gallery;
import styled from "styled-components";
function ForFourMultiplyFour({ images, setPage, page }) {
const handlePage = (param) => {
if (param === "plus") {
setPage(page + 1);
} else {
if (page === 1) return;
setPage(page - 1);
}
};
if (images.length < 1) return <></>;
return (
<>
<ButtonWrapper>
<Button onClick={() => handlePage("minus")}>prev</Button>
<Button onClick={() => handlePage("plus")}>next</Button>
</ButtonWrapper>
<div
style={{
display: "flex",
justifyContent: "center",
flexWrap: "wrap"
}}
>
{images.map((el) => {
return (
<div style={{ margin: "5px", flex: "1 1 20%" }} key={el.id}>
<Img key={el.id} src={el.download_url} alt="picture"></Img>
<div style={{ display: "flex", justifyContent: "space-between" }}>
<div>생성일자</div>
</div>
</div>
);
})}
</div>
</>
);
}
const Img = styled.img`
width: 15vw;
height: 20vh;
`;
const ButtonWrapper = styled.div`
display: flex;
`;
const Button = styled.button`
cursor: pointer;
`;
export default ForFourMultiplyFour;
You can see the result through this Code Sandbox link.
If it is not a solution that you wanted, please comment me. Then I'll try to help you using another solution.

Related

Sync scroll react. div block with main scroll on window

I want to synchronize a divs scroll with a body scroll.
I tried some examples with two divs but I couldn't manage fix it with the body scroll.
Sample code with two divs: https://codesandbox.io/s/react-custom-scroll-sync-of-2-divs-10xpi
My Code
https://codesandbox.io/s/funny-rain-ditbv
import "./styles.css";
import { useRef } from "react";
export default function App() {
const firstDivRef = useRef();
const secondDivRef = useRef();
const handleScrollFirst = (scroll) => {
secondDivRef.current.scrollTop = scroll.target.scrollTop;
};
const handleScrollSecond = (scroll) => {
firstDivRef.current.scrollTop = scroll.target.scrollTop;
};
return (
<div
className="App"
style={{
display: "flex",
}}
>
<div
onScroll={handleScrollFirst}
ref={firstDivRef}
style={{
height: "500px",
overflow: "scroll",
backgroundColor: "#FFDAB9",
position: "sticky",
top: "0px"
}}
>
<div style={{ height: 5000, width: 300 }}>
The first div (or it can be tbody of a table and etc.)
{[...new Array(1000)].map((_, index) => {
const isEven = index % 2 === 0;
return (
<div style={{ backgroundColor: isEven ? "#FFFFE0 " : "#FFDAB9" }}>
{index}
</div>
);
})}
</div>
</div>
<div
onScroll={handleScrollSecond}
ref={secondDivRef}
style={{
height: "100%",
backgroundColor: "#EEE8AA"
}}
>
<div style={{ height: 5000, width: 200 }}>
The second div
{[...new Array(1000)].map((_, index) => {
const isEven = index % 2 === 0;
return (
<div style={{ backgroundColor: isEven ? "#FFFFE0 " : "#FFDAB9" }}>
{index}
</div>
);
})}
</div>
</div>
</div>
);
}
It was easy to use different divs rather than using a div and window.
But finally managed to run it with a div and the body.
The trick is they block each other since they listen each others values.
import "./styles.css";
import { useEffect, useRef, useState } from "react";
export default function App() {
const firstDivRef = useRef();
const [scrollTop, setScrollTop] = useState(0);
const [disableBodyScroll, setDisableBodyScroll] = useState(false);
const handleScrollFirst = (scroll) => {
setScrollTop(scroll.target.scrollTop);
};
useEffect(() => {
if (firstDivRef.current && !disableBodyScroll) {
firstDivRef.current.scrollTop = scrollTop;
}
if (disableBodyScroll) {
window.scrollTo(0, scrollTop);
}
}, [firstDivRef, scrollTop, disableBodyScroll]);
useEffect(() => {
const onScroll = () => {
console.log(disableBodyScroll, window.scrollY);
if (!disableBodyScroll) {
setScrollTop(window.scrollY);
}
};
// clean up code
window.removeEventListener("scroll", onScroll);
window.addEventListener("scroll", onScroll);
return () => window.removeEventListener("scroll", onScroll);
}, [disableBodyScroll]);
return (
<div
className="App"
style={{
display: "flex"
}}
>
<div
onMouseEnter={() => setDisableBodyScroll(true)}
onMouseLeave={() => setDisableBodyScroll(false)}
onScroll={handleScrollFirst}
ref={firstDivRef}
style={{
height: "500px",
overflow: "scroll",
backgroundColor: "#FFDAB9",
position: "sticky",
top: "0px"
}}
>
<div style={{ height: 5000, width: 300 }}>
The first div (or it can be tbody of a table and etc.)
{[...new Array(1000)].map((_, index) => {
const isEven = index % 2 === 0;
return (
<div style={{ backgroundColor: isEven ? "#FFFFE0 " : "#FFDAB9" }}>
{index}
</div>
);
})}
</div>
</div>
<div
style={{
height: "100%",
backgroundColor: "#EEE8AA"
}}
>
<div style={{ height: 5000, width: 200 }}>
The second div
{[...new Array(1000)].map((_, index) => {
const isEven = index % 2 === 0;
return (
<div style={{ backgroundColor: isEven ? "#FFFFE0 " : "#FFDAB9" }}>
{index}
</div>
);
})}
</div>
</div>
</div>
);
}
https://codesandbox.io/s/ancient-dream-tzuel?file=/src/App.js
Try the next example. This is a quick sketch but maybe it will help you.
https://codesandbox.io/s/gallant-goldwasser-19g4d?file=/src/App.js

How can I upload Images with Preview in React

I've been trying to upload multiple images WITH preview in NextJS (React). I tried changing the constants to arrays and tried mapping through them but it just doesn't seem to work and I don't know how I could get it to work.
I've made a component out of the upload functionality and here is the code that works for uploading a single image with a Preview.
uploadImage.js
import React, { useEffect, useRef, useState } from "react";
import styled from "styled-components";
function imageUpload() {
const [image, setImage] = useState(null);
const fileInputRef = useRef();
const [preview, setPreview] = useState();
useEffect(() => {
if (image) {
const reader = new FileReader();
reader.onloadend = () => {
setPreview(reader.result);
};
reader.readAsDataURL(image);
} else {
}
}, [image]);
return (
<div className="flex ">
<StyledImg
src={preview}
style={{ objectFit: "cover" }}
onClick={() => setImage(null)}
/>
<StyledButton
onClick={(e) => {
e.preventDefault();
fileInputRef.current.click();
}}
/>
<input
type="file"
style={{ display: "none" }}
accept="image/*"
ref={fileInputRef}
onChange={(e) => {
const file = e.target.files[0];
if (file && file.type.substr(0, 5) === "image") {
setImage(file);
} else {
setImage(null);
}
}}
/>
</div>
);
}
const StyledButton = styled.button`
`;
const StyledImg = styled.img`
width: 100px;
height: 100px;
margin-right: 10px;
`;
export default imageUpload;
Based on these references https://react-dropzone.js.org/#section-previews and https://stackblitz.com/edit/nextjs-buk2rw?file=pages%2Findex.js I replaced my code with the following
ImageUpload.js
import React, { useEffect, useState } from "react";
import { useDropzone } from "react-dropzone";
import styled from "styled-components";
function DragAndDrop() {
const [files, setFiles] = useState([]);
const { getRootProps, getInputProps } = useDropzone({
accept: "image/*",
onDrop: (acceptedFiles) => {
setFiles((files) => [
...files,
...acceptedFiles.map((file) =>
Object.assign(file, {
key: file.name + randomId(), // to allow adding files with same name
preview: URL.createObjectURL(file),
})
),
]);
},
});
const removeFile = (file) => {
setFiles((files) => {
const newFiles = [...files];
newFiles.splice(file, 1);
return newFiles;
});
};
const thumbs = files.map((file, i) => (
<div style={thumb} key={file.key}>
<div style={thumbInner}>
<img src={file.preview} style={img} />
</div>
<button type="button" style={removeButton} onClick={() => removeFile(i)}>
X
</button>
</div>
));
useEffect(
() => () => {
files.forEach((file) => URL.revokeObjectURL(file.preview));
},
[files]
);
return (
<section className="container">
<div {...getRootProps({ className: "dropzone" })}>
<input {...getInputProps()} />
<StyledP className="flex align-center justify-center">
Glisser Images Ici ou Cliquer pour selectionner
</StyledP>
</div>
<aside style={thumbsContainer}>{thumbs}</aside>
</section>
);
}
const StyledP = styled.p`
cursor: pointer;
padding: 30px;
`;
const randomId = () => (Math.random() + 1).toString(36).substring(7);
const thumbsContainer = {
display: "flex",
flexDirection: "row",
flexWrap: "wrap",
marginTop: 16,
};
const thumb = {
display: "inline-flex",
borderRadius: 2,
border: "1px solid #eaeaea",
marginBottom: 8,
marginRight: 8,
width: 100,
height: 100,
padding: 4,
boxSizing: "border-box",
position: "relative",
};
const thumbInner = {
display: "flex",
minWidth: 0,
overflow: "hidden",
};
const img = {
display: "block",
width: "auto",
height: "100%",
};
const removeButton = {
color: "red",
position: "absolute",
right: 4,
};
export default DragAndDrop;

Change only state of clicked card

How can I get the clicked card only to change its string from 'not captured' to 'captured'? Right now, all cards' strings say 'captured' even if I click on only one. I think the problem is that the captured state updates for all the cards and I can't get the captured state to update for the single clicked card. It's an onChange event and a checkbox.
import React, { useState, useEffect } from 'react'
import PokemonCard from '../components/PokemonCard';
const Pokedex = () => {
const [pokemons, setPokemons] = useState([]);
const [captured, setCaptured] = useState(false )
const URL = 'https://pokeapi.co/api/v2/pokemon/?limit=151';
const fetchingPokemons = async () => {
const res = await fetch(URL);
const data = await res.json();
// console.log(data)
setPokemons(data.results)
}
useEffect(() => {
fetchingPokemons()
}, [URL])
const toggleCaptured= (e, id) => {
console.log(id)
if(id && e) {
console.log('oh')
setCaptured(captured => !captured)
}
let capturedPkm = [];
let notCapturedPkm = [];
pokemons.forEach(i => {
if(captured === true) {
capturedPkm.push(pokemons[i])
} else {
notCapturedPkm.push(pokemons[i])
}
})
console.log('captured', capturedPkm, 'not captured', notCapturedPkm)
}
return (
<>
<div style={{display: 'flex', flexWrap: 'wrap', justifyContent: 'space-evenly'}}>
{pokemons ? pokemons.map((pokemon) => {
return (
<>
<div style={{ width: '235px' }} >
<PokemonCard
pokemon={pokemon}
name={pokemon.name}
url={pokemon.url}
key={pokemon.id}
captured={captured}
toggleCaptured={toggleCaptured}
/>
</div>
</>
)
}) : <h1>Loading...</h1>}
</div>
</>
)
}
export default Pokedex
import React, { useState, useEffect } from 'react';
import { Link } from 'react-router-dom';
import PokemonIcon from './PokemonIcon';
const PokemonCard = (props) => {
const { url, captured, toggleCaptured } = props
const URL = url
const [pokemonCard, setPokemonCard] = useState([])
const fetchingPokemonCard = async () => {
const res = await fetch(URL);
const data = await res.json();
//console.log(data)
setPokemonCard(data)
}
useEffect(() => {
fetchingPokemonCard()
}, [URL])
return (
<>
<div className='pokemon-card' style={{
height: '250px',
maxWidth: '250px',
margin: '1rem',
boxShadow: '5px 5px 5px 4px rgba(0, 0, 0, 0.3)',
cursor: 'pointer',
}} >
<Link
to={{ pathname: `/pokemon/${pokemonCard.id}` }}
state={{ pokemon: pokemonCard, captured }}
style={{ textDecoration: 'none', color: '#000000' }}>
<div
style={{ padding: '20px', display: 'flex', justifyContent: 'center', alignItems: 'center' }} >
<PokemonIcon img={pokemonCard.sprites?.['front_default']} />
</div>
</Link>
<div style={{ textAlign: 'center' }}>
<h1 >{pokemonCard.name}</h1>
<label >
<input
type='checkbox'
defaultChecked= {captured}
onChange={(e) => toggleCaptured(e.target.checked, pokemonCard.id)}
/>
<span style={{ marginLeft: 8, cursor: 'pointer' }}>
{captured === false ? 'Not captured!' : 'Captured!'}
{console.log(captured)}
</span>
</label>
</div>
</div>
<div>
</div>
</>
)
}
export default PokemonCard
Use an object as state:
const [captured, setCaptured] = useState({})
Set the toggle function:
const toggleCaptured = (checked, id) => {
const currentChecked = { ...captured }; // Create a shallow copy
console.log(id)
if (checked) {
currentChecked[id] = true;
} else {
delete currentChecked[id];
}
setCaptured(currentChecked); // Update the state
let capturedPkm = pokemons.filter(({id}) => currentChecked[id]);
let notCapturedPkm = pokemons.filter(({id}) => !currentChecked[id]);
console.log('captured', capturedPkm, 'not captured', notCapturedPkm)
}
The PokemonCard should look like this
<PokemonCard
pokemon={pokemon}
name={pokemon.name}
url={pokemon.url}
key={pokemon.id}
captured={captured[pokemon.id]} // Here
toggleCaptured={toggleCaptured}
/>

React page won't load mapped elements until calling them again

I have a React dApp for a smart contract that I have made. In one of the routes of the application which I go by clicking a button named "All Cryptonauts" as seen in the screenshow below, I try to call all of the minted NFT's of my smart contract. I can successfully get them and map them all, but at first, nothing comes up.
However, after clicking the "All Cryptonauts" button again, all of the intended data gets shown.
Below, there are the codes of my page. I think my problem is with rendering, so I have made some research and someone said that they avoid to manually rerender and fix an identical issue with removing the key attributes from the HTML codes, but it didn't work for me and there were errors in the console when I removed the keys. I can't use this.setState here, too. Can anyone help me with the right way to do what I want please? Thank you!
export const AllCryptonauts = (props) => {
const web3 = window.web3;
var [currentAccount, setCurrentAccount] = useState("0x0");
let [model, setModel] = useState([]);
let lastMintJson;
let supply = [];
let myNFTs = [];
useEffect(() => {
window.ethereum.on('chainChanged', (_chainId) => checkChainID());
window.ethereum.on('accountsChanged', (_accounts) => loadBlockchainData());
checkChainID();
return () => { }
}, [currentAccount])
async function checkChainID() {
const networkId = await web3.eth.net.getId();
if (networkId !== 4) {
props.history.push("/")
} else {
loadBlockchainData();
}
}
async function loadBlockchainData() {
window.web3 = new Web3(window.ethereum);
const accounts = await web3.eth.getAccounts();
setCurrentAccount(accounts[0]);
loadContract();
}
async function loadContract() {
if (currentAccount.length > 5) {
const ContractObj = impContract;
supply = await ContractObj.methods.totalSupply().call();
setAllMints(supply);
}
}
async function setAllMints(supply) {
for (var i = 1; i <= parseInt(supply, 10); i++) {
lastMintJson = "https://cors-anywhere.herokuapp.com/https://nftornek.000webhostapp.com/cryptonauts/json/" + i + ".json";
let res = await axios.get(lastMintJson);
res.data.imagelink = "https://nftornek.000webhostapp.com/cryptonauts/image/" + i + ".png"
myNFTs.push(res.data);
}
setModel(setNFTModel(myNFTs));
}
function setNFTModel(jsonObj) {
for (var i = 0; i < jsonObj.length; i++) {
model[i] = {
dna: jsonObj[i].dna,
name: jsonObj[i].name,
edition: jsonObj[i].edition,
imagelink: jsonObj[i].imagelink,
attributes: jsonObj[i].attributes
};
}
return model;
}
return (
<div>
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}><img src="https://nftornek.000webhostapp.com/frontend/cnlogo.png" width='500' height='180' alt=""></img></div>
<div style={{ display: 'flex', justifyContent: 'center' }}>
<button className="regularButton divide" onClick={MintPage}>Mint</button>
<button className="regularButton divide" onClick={MyCryptonauts}>My Cryptonauts</button>
<button className="regularButton divide" onClick={AllCryptonauts}>All Cryptonauts</button>
<button className="regularButton divide" onClick={Disconnect}>Disconnect</button>
</div>
<div style={{ display: 'flex', justifyContent: 'center' }}><p className="accountText">Current Account: {currentAccount}</p></div>
{model.map((item, i) => (
<div key={i} style={{ display: 'flex', justifyContent: 'center', marginBottom: '30px', height: '350px' }}>
<div style={{ width: '350px', border: '2px solid #38495a', borderRadius: '5px' }}><img src={item.imagelink} alt=""></img>
</div>
<div style={{ width: '300px', padding: '10px', border: '2px solid #38495a', borderRadius: '4px', backgroundColor: 'rgba(56, 73, 90, 0.25)', color: '#38495a' }}><b>ID: {item.edition}<br></br> Name: {item.name}</b>
<table className="tableClass t1">
<tbody>
{item.attributes.map((attr, j) => (
<tr key={'attr' + ' ' + j}>
<td key={1}>{attr.trait_type}:</td>
<td key={2}>{attr.value}</td>
</tr>
))}
</tbody>
</table></div>
</div>
))}
</div>
)
}
Try something like this:
{model && model.map((item, i) => (
I think as well that your data(state) is not available at the moment that the page is rendered
I have fixed the error by following this answer by ray from Why is useState not triggering re-render?:
You're calling setNumbers and passing it the array it already has. You've changed one of its values but it's still the same array, and I suspect React doesn't see any reason to re-render because state hasn't changed; the new array is the old array.
One easy way to avoid this is by spreading the array into a new array:
setNumbers([...old])
Now it works.

react-spring and react-intersection-observer - tons of rerenders

JSFiddle
Code:
export default function App() {
const spring = useSpring({ from: { opacity: 0 }, to: { opacity: 1 } });
const [ref] = useInView();
rerenders++;
return (
<div style={{ height: "200vh" }}>
<div style={{ height: "150vh" }}></div>
<animated.div
ref={ref}
style={{
height: "50px",
width: "50px",
backgroundColor: "red",
opacity: spring.opacity
}}
>
Hello!
</animated.div>
</div>
);
}
Attaching useInView's ref (a hook from react-intersection-observer) causes constant rerendering of the component. Why is that so?
Using an IntersectionObserver yourself does not do such a thing:
const ref = useRef<any>();
useLayoutEffect(() => {
const obs = new IntersectionObserver((entries, observer) => {
entries.forEach((entry, index) => {
console.log(entry);
});
});
obs.observe(ref.current);
}, []);

Resources