I have a view with content that I get with searchContent. I map the received items in the return, and then I want to call a function in useEffect. The problem I have is that the function sets the height of an element to the height of its children, but the content of the children isn't loaded completely before the useEffect is executed. Because of this, the height is too small, and the child elements won't display correctly. As long as I have static content in the return, everything works properly. I thought that useEffect was getting called after the content had loaded, or am I wrong?
The problem occurs when I reload the page, as soon as I click on a button to filter the items, the height is correct.
This is how the elements look.
This is how it should look like.
This is the useEffect function
The view
import React, { useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { searchContent } from '@plone/volto/actions';
import PropTypes from 'prop-types';
import { Helmet } from '../../../../../omelette/src/helpers/Helmet/Helmet';
import 'remixicon/fonts/remixicon.css'
const getPeopleContent = (array = []) =>
array.map((obj, item) => {
obj[0] = item;
return obj;
}, {}
);
const IsotopeView = ({ data }) => {
const dispatch = useDispatch();
const people = useSelector((state) =>
getPeopleContent(state.search.subrequests.person?.items),
);
const content = useSelector((state) => state.workflow.transition);
useEffect(() => {
dispatch(
searchContent(
'/',
{
portal_type: ['person'],
review_state: 'private', //Has to be changed to public when deploying
fullobjects: true,
},
'person',
),
);
}, [dispatch, content]);
// init one ref to store the future isotope object
var isotope;
if (typeof window !== "undefined") {
isotope = window.Isotope;
}
const handleClick = event => {
const buttons = document.getElementsByClassName("is-checked");
console.log(buttons)
Array.prototype.forEach.call(buttons, function(button) {
button.classList.remove('is-checked');
})
event.currentTarget.classList.toggle('is-checked');
};
// store the filter keyword in a state
const [filterKey, setFilterKey] = useState('*')
const handleFilterKeyChange = key => (event) => {setFilterKey(key); handleClick(event);}
// initialize an Isotope object with configs
useEffect(() => {
isotope.current = new Isotope('.grid', {
itemSelector: '.grid-item',
sortBy: 'random',
})
handleFilterKeyChange('*')
// cleanup
return () => isotope.current.destroy()
})
// handling filter key change
useEffect(() => {
filterKey === '*'
? isotope.current.arrange({filter: `*`})
: isotope.current.arrange({filter: `.${filterKey}`})
}, [filterKey])
return(
<>
<Helmet>
<script src="https://unpkg.com/isotope-layout@3/dist/isotope.pkgd.min.js"></script>
</Helmet>
<div id="filters" className="button-group filter-button-group">
<button className='filterBtn is-checked' onClick={handleFilterKeyChange('*')}>Alle</button>
{data.default_tags.map((filter) => (
<button className='filterBtn' onClick={handleFilterKeyChange(filter)}>{ filter }</button>
))}
</div>
<hr />
<div className='grid'>
{people.map((person) => (
<div className={'grid-item ' + person.tags_field.map((tag) => (tag.token))} key={person.UID}>
<img className='gridImg' src={person.img_field.download} alt={person.img_field.filename} />
<div className='girdContent'>
<p className='gridContentName'>{ person.name_field }</p>
<p>{ person.position_field }</p>
<p>{ person.entrance_field }</p>
<a href={'mailto:' + person.email_field}><i className="ri-mail-line"></i></a>
{person.tags_field.map((tag, i) => (
<button className='gridContentTag' key={person.UID + i}>{ tag.token }</button>
))}
</div>
</div>
))}
</div>
</>
);
};
IsotopeView.propTypes = {
data: PropTypes.objectOf(PropTypes.any).isRequired,
};
export default IsotopeView;