Component returning nested React Elements not displaying - javascript

I have a default component Collection which uses a sub-component called RenderCollectionPieces to display UI elements. I can't figure out why I am able to see the data for image.name in the console but not able to see the UI elements display.
Additional information:
There are no errors in the console
If I replace <p>{image.name}</p> with <p>TESTING</p>, still nothing shows.
columnOrg is a list of lists
each list in columnOrg is a list of maps with some attributes
Index.js:
const RenderCollectionPieces = () => {
const {collectionId} = useParams();
let listOfImageObjects = collectionMap[collectionId];
let imagesPerColumn = Math.ceil(listOfImageObjects.length / 4);
let columnOrg = [];
while (columnOrg.length < 4){
if(imagesPerColumn > listOfImageObjects.length){
imagesPerColumn = listOfImageObjects.length;
}
columnOrg.push(listOfImageObjects.splice(0,imagesPerColumn))
}
let collectionList = columnOrg.map((col) => {
return(
<Grid item sm={3}>
{
col.map((image) => {
console.log(image.name)
return(
<p>{image.name}</p>
)
})
}
</Grid>
)
});
return collectionList;
};
const Collection = ({ match }) => {
const {collectionId} = useParams();
return(
<Box sx={{ background:'white'}}>
<Grid container>
<RenderCollectionPieces />
</Grid>
</Box>
)
};
export default Collection;

I think you are misunderstanding state management in React. Every variable you want to remember inbetween component re-renders should be included in state using useState hook. If you want to perform something initially like your while loop, use it inside useEffect hook.
const MyComponent = () => {
const [myCounter, setMyCounter] = useState(0);
useEffect(() => {
console.log("This will be performed at the start");
}, []);
return (
<Fragment>
<button onClick={() => setMyCounter(myCounter++)} />
You clicked {myCounter} times
</Fragment>
)
}
If you are unfamiliar with useState and useEffect hooks I recommend learning about them first to understand how React manages state and re-renders: https://reactjs.org/docs/hooks-intro.html

Got it to work by using useEffect/useState as recommended by Samuel Oleksak
const RenderCollectionPieces = (props) => {
const [columnOrg, setColumnOrg] = useState([]);
useEffect(() => {
let columnSetup = []
let listOfImageObjects = collectionMap[props.collectionId.collectionId];
let imagesPerColumn = Math.ceil(listOfImageObjects.length / 4);
while (columnSetup.length < 4){
if(imagesPerColumn > listOfImageObjects.length){
imagesPerColumn = listOfImageObjects.length;
}
columnSetup.push(listOfImageObjects.splice(0,imagesPerColumn))
}
setColumnOrg(columnSetup);
},[]);
return (
columnOrg.map((column) => {
return (
<Grid item sm={3}>
{
column.map((image) => {
return (<img src={image.src} alt={image.name}/>)
})
}
</Grid>
)
})
)
};

Related

How to use a value changed by useEffect?

So I have my code like this:
var problems = ['a','b','c'];
var allProblemStatus;
var selectProblemStatus = "";
useEffect(() => {
let getProblemStatus = async() => {
let response = await fetch('http://127.0.0.1:8000/api/problem-status/');
allProblemStatus = await response.json();
selectProblemStatus = allProblemStatus['problem_status'];
}
getProblemStatus();
}, []);
return (
<div>
{problems.map((problem, index) => (
<Grid item xs={200} md={100} lg={5}>
<Problem key={index} problem={problem} a={selectProblemStatus} />
</Grid>
))}
</div>
);
selectProblemStatus is being changed in useEffect but how do I actually use it to pass it to the Problem component as a prop, also is there a way to console.log the changed selectProblemStatus
it is clear that you are unfamiliar with useState hook in react.
your approach should be look like this:
import { useState } from 'react'
const YourComponent = (props) => {
const [problems, setProblems] = useState([])
const getProblemStatus = async () => { ... }
useEffect(() => {
getProblemStatus()
}, [])
return (
<div>
{problems.map((problem, index) => (
<Grid key={index} item xs={200} md={100} lg={5}>
<Problem problem={problem} a={selectProblemStatus} />
</Grid>
))}
</div>
)
}
You can use useState() hook for your variables, and then in useEffect update them. Here is link how to use useState https://uk.reactjs.org/docs/hooks-state.html

change in one item re runs whole map component loop in ReacJS

I am dispatching an add comment action on a specific post re runs component loop again instead of updating a specific one. Suppose, If I have 100 posts adding comments to one post runs the component loop again and iterates again 100 times. Is there is any way to re-render only a specific item instead of running the whole component loop again?
Here's my code of the looped component
const Post = ({totalComments, like, _id, image, caption}) => {
const {enqueueSnackbar} = useSnackbar();
const dispatch = useDispatch();
const { user } = useSelector((state) => state.loadUser);
const { loading: loadingComments, comments } = useSelector((state) => state.listComments);
const { success: addCommentSucess, error: addCommentError } = useSelector((state) => state.addComment);
const [openComment, setOpenComment] = useState('');
const [commentsLength, setCommentsLength] = useState(totalComments);
//listing of comments
useEffect(() => {
if (addCommentError) {
enqueueSnackbar(addCommentError, {variant: 'error'});
dispatch(clearErrors());
}
}, [addCommentError, addCommentSucess, dispatch, enqueueSnackbar]);
const openCommentHandler = () => {
dispatch(listComments(_id));
setOpenComment(!openComment);
}
const addCommentHandler = (data) => {
console.log('addcomehandle')
dispatch(addComment(data));
setCommentsLength(commentsLength+1);
}
return (
<Card className='post-container'>
<div className='button-wrapper'>
<IconButton onClick={openCommentHandler} color={openComment ? 'primary' : 'default'}>
<SvgIcon component={CommentOutlinedIcon} />
</IconButton>
<p>{commentsLength}</p>
</div>
{!loadingComments && openComment && (
<Comments comments={comments} />
)}
<Divider />
<AddComment onAddComment={addCommentHandler} postId={_id} />
</Card>
);
};
export default Post;

How to update a component in a ES6 map function in React stateless component?

I have a functional component that is a modal. Inside I am doing a map on a array to render picture. I would like to add a div on the picture of one picture when clicking on it. However, it appears that the UI is not updated inside of the map even when using useState. Any idea on how to solve the issue?
Here is the code (I removed Style and things that were not important):
const CreateBoardModal = ({ closeModal, isModalOpen, searchResults, ...props }) => {
Modal.setAppElement('body')
const [movies, setMovies] = useState([])
const [searchResultsAdded, setSearchResultAdded] = useState({})
const addMovie = (movieId) => {
if (movies.includes(movieId)) {
console.log("Already in the list")
} else {
console.log("Added to the list!")
var movies_temp = movies
movies_temp.push(movieId)
setMovies(movies_temp)
}
var searchMoviesTemp = searchResultsAdded
searchMoviesTemp[movieId] = true
setSearchResultAdded(searchMoviesTemp)
console.log(searchResultsAdded) //Here everything is updated as expected
}
return (
<Modal
isOpen={isModalOpen}
onRequestClose={closeModal}
>
<div>
{searchResults.length > 0 &&
searchResults.map((movie, index) => {
return (
<div style={{ margin: 10 }} onClick={() => addMovie(movie.id)}>
<Movie
title={movie.original_title}
voteAverage={movie.vote_average}
posterPath={movie.poster_path}
></Movie>
{searchResultsAdded[movie.id] &&
<div>Added ✓</div> } {/*This is shown only when I hot reload the react app*/}
</div>
)
})}
</div>
</Modal >
)
}
export default CreateBoardModal;

How do i concat an array with a different array which uses of local storage

Datalist is an array I'm trying to concat the boards array with the Datalist array, but when I console it doesn't reflect. On the other hand when I assign Datalist.concat(boards) to a variable it reflects example
const newArr = Datalist.concat(boards);
console.log(newArr)
(main code) please help me review it. Thanks in advance
import React, { useState, useEffect } from 'react';
import Modal from './Modal';
import { Datalist } from '../Data/Boards';
function Boards() {
const [boards, setboards] = useState(JSON.parse(localStorage.getItem('boards')) || []);
const [title, settitle] = useState('');
localStorage.setItem('boards', JSON.stringify(boards));
Datalist.concat(boards);
console.log(Datalist);
const handleChange = (e) => {
settitle(e.target.value);
};
const handleSubmit = () => {
if (title.length === 0) {
return;
}
setboards((prev) => [...prev, title]);
};
return (
<div>
<ul id="boards">
<BoardList boards={boards} />
</ul>
<Modal title={title} handleChange={handleChange} handleSubmit={handleSubmit} />
</div>
);
}
function BoardList({ boards }) {
const history = useHistory();
return (
<>
{boards.map((board, index) => (
<li
key={index}
onClick={() => {
history.push('./workspace');
}}
>
<h3>{board}</h3>
</li>
))}
</>
);
}
export default Boards;
That is the expected behaviour. The concat function does not alter the original arrays. You can read about it in the MDN docs
For your case you should be able to do Datalist = Datalist.concat(boards); and it should work like you're expecting

How can I prevent compound component from re-rendering?

I am using react context, and all it contains at the moment are 3 items: contacts and editingContact, and editContact:
interface ContactsContextProps {
contacts: Contact[];
editingContact: Contact;
editContact: (contact: Contact) => () => void // being lazy and this is from an onClick
}
const ContactsContext = React.createContext<Partial<ContactsContextProps>>({
editContact: (contact: Contact) => () => {}
})
const ContactsProvider: React.FunctionComponent = props => {
const [contacts, setContacts] = useState<Contact[]>();
const [editingContact, setEditingContact] = useState<Contact>();
React.useEffect(() => {
// fetch contacts, and setContacts(contacts)
}, [])
const editContact = React.useCallback((contact: Contact) => {
return function() {
setEditingContact(contact);
}
})
return (
<ContactsContext.Provider
value={{
editingContact,
editContact,
contacts
}}
>
{props.children}
</ContactsContext.Provider>
)
}
Here's how it is being used:
const ContactsList: React.FunctionComponent<{
contacts: Contact[];
}> = React.memo(props => {
return (
<>
{props.contacts.map(contact => (
<Card key={contact.id} contact={contact} />
))}
</>
);
});
const Wrapper: React.FunctionComponent = () => {
const contactsCtx = React.useContext(ContactsContext);
return (
<>
<Box className={styles.main}>
<Header />
{contactsCtx.contacts && <ContactsList contacts={contactsCtx.contacts} />}
</Box>
{contactsCtx.editingContact && <EditContactModal />}
</>
);
};
The <Card /> only has an edit button right now, which calls contactsContext.editContact(). However, each time this is called, all the Cards re-render. I placed a console.log('card') in each Card, and it logs card 10 times (I have 10 contacts right now).
What am I doing wrong?
There has been a discussion in a React Github issue, basically there is 3 possible solutions for this:
Option 1 (Preferred): Split contexts that don't change together
Option 2: Split your component in two, put memo in between
Option 3: One component with useMemo inside
You should check the link for examples about it.

Categories