I'm building a ReactJS Component that uses React Awesome Slider. What I'm trying to create is a slider with a description div under it, which changes the text then I change the Slide.
Now, I found a way to make it work but I have a problem with the setState of an object, here is the code.
SLIDER:
const AutoplaySlider = withAutoplay(AwesomeSlider);
const StaticSlider = ({slider}) => {
var images = "";
var length=0;
const [current, setCurrent] = useState(0);
const [title, setTitle] = useState([]);
const [description, setDescription] = useState([]);
switch (slider) {
case 'portfolio_sviluppo_software':
images = portfolio_description.sviluppo_software;
length= portfolio_description.sviluppo_software.length;
break;
case 'portfolio_domotica':
images = portfolio_description.domotica;
length= portfolio_description.domotica.length;
break;
case 'portfolio_digital_signage':
images = portfolio_description.digital_signage;
length= portfolio_description.digital_signage.length;
break;
case 'portfolio_ricerca_e_sviluppo':
images = portfolio_description.ricerca_e_sviluppo;
length= portfolio_description.ricerca_e_sviluppo.length;
break;
}
useEffect(
() => {
setTitle(
images.map(
(slide) => (slide.title)
)
);
setDescription(
images.map(
(desc) => (desc.data)
)
);
}, [images]
);
return(
<div>
<AutoplaySlider
play={true}
cancelOnInteraction={true}
interval={0}
onTransitionStart={slide => setCurrent(slide.nextIndex)}
className="sliderHome"
>
{images.map((image, index) => {
let src = "/image/slider/portfolio/" image.image;
//console.log(src);
return (
<div key={index} data-src={src}>
</div>
);
})}
</AutoplaySlider>
<GalleryCaption selected={current} title={title} description={description} area={slider}/>
</div>
)
};
export default StaticSlider;
DESCRIPTION GENERATOR
const GalleryCaption = ({ selected = 0, title = [], description= [], area = 0 }) => {
const formattedIndex = selected 1;
var title = title[selected];
var data = description[selected];
return (
<div className="containerDivDescriptionPortflio">
<div className="DivDescriptionPortflio">
<p id ={"description_portfolio_" area} className="paragDescriptionPortflio" >
<h4>{title}</h4>
<hr></hr>
{
data.map((val) => (
<div className="rowDescriptionPortfolio">
<div className="divIndexPortfolio" dangerouslySetInnerHTML={{ __html: val.index }} >
</div>
<div className="divTextPortfolio" dangerouslySetInnerHTML={{ __html: val.text }} >
</div>
</div>
))}
</p>
</div>
</div>
);
};
export default GalleryCaption;
OBJECT EXAMPLE
{
"title":"text",
"data":[
{
"index":"text",
"text": "text"
},
{
"index":"text",
"text": "text"
}
],
"image": "folder/image.jpg"
},
(This is an element of an array of this kind of object) Now the main problem is that if inside the use effect I only call the setTitle function all works as it should, but if I use also the setDescription all just stop working. I didn't get a specific error, but I get a white screen.
ERROR THAT I GET
Warning: Each child in a list should have a unique "key" prop.
Check the render method of `PortfolioArea`. See https://reactjs.org/link/warning-keys for more information.
at div
at PortfolioArea (http://localhost:3000/static/js/bundle.js:1618:5)
at http://localhost:3000/static/js/bundle.js:4509:78
at Routes (http://localhost:3000/static/js/bundle.js:177110:5)
at Router (http://localhost:3000/static/js/bundle.js:177043:15)
at BrowserRouter (http://localhost:3000/static/js/bundle.js:176523:5)
at App
at AppProvider (http://localhost:3000/static/js/bundle.js:3289:5)
Warning: Using UNSAFE_componentWillReceiveProps in strict mode is not recommended and may indicate bugs in your code. See https://reactjs.org/link/unsafe-component-lifecycles for details.
* Move data fetching code or side effects to componentDidUpdate.
* If you're updating state whenever props change, refactor your code to use memoization techniques or move it to static getDerivedStateFromProps. Learn more at: https://reactjs.org/link/derived-state
Please update the following components: AwesomeSlider
The above error occurred in the <GalleryCaption> component:
at GalleryCaption (http://localhost:3000/static/js/bundle.js:973:5)
at div
at StaticSlider (http://localhost:3000/static/js/bundle.js:3049:5)
at div
at PortfolioArea (http://localhost:3000/static/js/bundle.js:1618:5)
at http://localhost:3000/static/js/bundle.js:4510:78
at Routes (http://localhost:3000/static/js/bundle.js:177111:5)
at Router (http://localhost:3000/static/js/bundle.js:177044:15)
at BrowserRouter (http://localhost:3000/static/js/bundle.js:176524:5)
at App
at AppProvider (http://localhost:3000/static/js/bundle.js:3290:5)
Consider adding an error boundary to your tree to customize error handling behavior. Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.`
I've tried to change the useEffect second parameters to null and also to use a unique state for every parameter, but the problem seems to be that every time I try to set a state with an object inside the useEffect, on the first render I always get a null value inside that state.
Any tips?
CodePudding user response:
I think that in StaticSlider
, since images
and length
are calculated based on slider
prop, I suggest using useMemo()
to calculate them per slider
change, instead of reassigning the values to the var
variables, which is not how it should be done in React and invites bugs to come.
const StaticSlider = ({slider}) => {
const images = useMemo(
() => {
// calculate and return images value
// with `switch`
switch (slider) {
// ...
default:
return [];
}
},
[slider]
);
const length = useMemo(
() => {
// calculate and return length value
// with `switch`
switch (slider) {
// ...
default:
return 0;
}
},
[slider]
);
Please note that your current switch
block does not have a default
case, you should consider returning a default case with initial values for images
and length
.
Also note that the initial assignment of images
is images = ""
which would make it a string
, but inside setDescription()
you are calling images.map()
which is an array method, so it won't work at the initial render of the component when images
is an empty string. I think this is what causes the bug.
images = []
or default: return []
for images inside switch
statement should be better.
Lastly I think you can consider using a condition check inside useEffect()
to only setState
on title
and description
when images
is already populated (has length).
useEffect(
() => {
if (images.length) {
setTitle(
images.map(
(slide) => (slide.title)
)
);
setDescription(
images.map(
(desc) => (desc.data)
)
);
}
},
[images]
);