11
2- import React from "react" ;
2+ import React , { useRef , useState , useLayoutEffect } from "react" ;
3+ // NOTE: useLayoutEffect is used intentionally here (instead of useEffect) so
4+ // that we can compute the correct `slidesToShow` value before the browser
5+ // paints the component. This avoids a visual flash where the slider briefly
6+ // renders with the desktop slide count and then corrects itself on mount.
37import styled from "styled-components" ;
48import Customers from "../../reusecore/Blockquote/Blockquote-image" ;
59import Slider from "react-slick" ;
@@ -33,28 +37,22 @@ const settings = {
3337 {
3438 breakpoint : 1300 ,
3539 settings : {
36- slidesToShow : 2.5 ,
40+ slidesToShow : 2 ,
41+ slidesToScroll : 1 ,
3742 }
3843 } ,
3944 {
4045 breakpoint : 1024 ,
4146 settings : {
4247 slidesToShow : 2 ,
48+ slidesToScroll : 1 ,
4349 }
4450 } ,
4551 {
4652 breakpoint : 800 ,
47- settings : {
48- slidesToShow : 1.5 ,
49- slidesToScroll : 0.5 ,
50- }
51- } ,
52- {
53- breakpoint : 600 ,
5453 settings : {
5554 slidesToShow : 1 ,
5655 slidesToScroll : 1 ,
57- autoplaySpeed : 2000 ,
5856 }
5957 }
6058 ]
@@ -81,15 +79,101 @@ max-width: 100%;
8179 opacity: 1;
8280 }
8381
82+ /* Prevent extreme shrinking on mobile */
83+ .slick-slide > div { min-width: 0; }
84+ .slider .type-one-wrapper { width: 100%; max-width: none; margin: 0 0.5rem; }
85+
86+ @media (max-width: 768px) {
87+ .type-one-wrapper.type-one-wrapper-boxed { max-width: none; padding: 0; }
88+ .type-one-quote .type-one-quote-base,
89+ .type-two-quote .type-two-quote-base { padding-left: 30px; padding-right: 30px; }
90+ }
91+
8492` ;
8593
8694
8795const Reviews = ( ) => {
96+ const [ isClient , setIsClient ] = useState ( false ) ;
97+ const [ slidesToShowState , setSlidesToShowState ] = useState ( null ) ;
98+ const sliderRef = useRef ( null ) ;
99+
100+ /* Merge runtime slidesToShow into settings and disable internal responsive handling.
101+ We disable the internal `responsive` array here because we compute the
102+ desired `slidesToShow` at runtime (based on window width) and pass it
103+ directly to the slider to avoid conflicting calculations and race
104+ conditions that can occur during initial mount. */
105+ const mergedSettings = {
106+ ...settings ,
107+ slidesToShow : slidesToShowState || 1 ,
108+ slidesToScroll : 1 ,
109+ responsive : [ ]
110+ } ;
111+
112+ /* computeSlides: determine the appropriate slidesToShow based on the
113+ current window width. This centralises the breakpoint logic so we can
114+ compute the initial value synchronously and reuse it in resize handlers. */
115+ const computeSlides = ( ) => {
116+ const w = typeof window !== "undefined" ? ( window . innerWidth || document . documentElement . clientWidth ) : 1200 ;
117+ if ( w <= 800 ) return 1 ;
118+ if ( w <= 1024 ) return 2 ;
119+ return 3 ;
120+ } ;
121+
122+ useLayoutEffect ( ( ) => {
123+ /* Initialise slider state synchronously before paint to avoid a flash of
124+ incorrect layout. We set slidesToShowState here so the Slider mounts with
125+ the right number of slides on initial render.
126+
127+ onResizeDebounced: debounced handler that recomputes slides and asks
128+ the react-slick instance to remeasure (via onWindowResized). Debounce
129+ helps avoid rapid repeated calls during a resize gesture.
130+
131+ onLoad: we also listen for the window "load" event and each slide image
132+ "load" event because images can change the layout width; when an image
133+ finishes loading we rerun the debounced handler to ensure the slider
134+ recalculates correctly.
135+ */
136+ setIsClient ( true ) ;
137+ setSlidesToShowState ( computeSlides ( ) ) ;
138+
139+ let resizeTimeout = null ;
140+ const onResizeDebounced = ( ) => {
141+ clearTimeout ( resizeTimeout ) ;
142+ resizeTimeout = setTimeout ( ( ) => {
143+ const slides = computeSlides ( ) ;
144+ setSlidesToShowState ( ( prev ) => {
145+ if ( prev !== slides ) return slides ;
146+ return prev ;
147+ } ) ;
148+ if ( sliderRef . current && sliderRef . current . innerSlider && typeof sliderRef . current . innerSlider . onWindowResized === "function" ) {
149+ sliderRef . current . innerSlider . onWindowResized ( ) ;
150+ }
151+ } , 100 ) ;
152+ } ;
153+
154+ const onLoad = ( ) => onResizeDebounced ( ) ;
155+
156+ window . addEventListener ( "resize" , onResizeDebounced ) ;
157+ window . addEventListener ( "load" , onLoad ) ;
158+ const imgs = document . querySelectorAll ( ".slider img" ) ;
159+ imgs . forEach ( ( img ) => img . addEventListener ( "load" , onLoad ) ) ;
160+
161+ return ( ) => {
162+ window . removeEventListener ( "resize" , onResizeDebounced ) ;
163+ window . removeEventListener ( "load" , onLoad ) ;
164+ imgs . forEach ( ( img ) => img . removeEventListener ( "load" , onLoad ) ) ;
165+ clearTimeout ( resizeTimeout ) ;
166+ } ;
167+ } , [ ] ) ;
168+
169+ if ( ! isClient || slidesToShowState === null ) return null ;
170+
88171 return (
89172 < ReviewsWrapper >
90173 < div className = "slider" >
91174 < h2 > Hear what other users have to say...</ h2 >
92- < Slider { ...settings } >
175+ { /* key property will force React to remount the Slider when slidesToShowState changes. */ }
176+ < Slider key = { `review-slider-${ slidesToShowState } ` } ref = { sliderRef } { ...mergedSettings } >
93177 { /* <Customers
94178 type="1"
95179 quote="The Meshery Extension transforms Docker Desktop into a powerful load generation utility, conveniently enabling me to deploy and configure any service mesh with a click of the button and invoke and control load-based performance tests from my desktop."
0 commit comments