1
- import React , { useRef , createRef , useMemo , useImperativeMethods , useState , useEffect } from 'react' ;
1
+ import React , { useRef , createRef , forwardRef , useImperativeMethods , useState , useEffect } from 'react' ;
2
2
import scrollIntoView from 'scroll-into-view-if-needed' ;
3
- import FoobarIpsum from 'foobar-ipsum' ;
4
3
5
4
function useAccordion ( panelsCount ) {
6
5
const [ currentIndex , setCurrentIndex ] = useState ( ) ;
@@ -15,14 +14,14 @@ function useAccordion(panelsCount) {
15
14
refs [ i ] = createRef ( ) ;
16
15
}
17
16
setRefs ( refs ) ;
18
- } , [ ] ) ;
17
+ } , [ ] ) ; // run once
19
18
20
19
useEffect ( ( ) => {
21
20
// Scroll current accordion panel into view
22
21
if ( currentIndex !== undefined ) {
23
22
refs [ currentIndex ] . current . scrollIntoView ( ) ;
24
23
}
25
- } , [ currentIndex ] ) ;
24
+ } , [ currentIndex ] ) ; // Run every time current index changes
26
25
27
26
function setCurrent ( newIndex ) {
28
27
setCurrentIndex ( currentIndex === newIndex ? undefined : newIndex ) ;
@@ -31,42 +30,32 @@ function useAccordion(panelsCount) {
31
30
return [ currentIndex , setCurrent , refs ] ;
32
31
}
33
32
34
- const AccordionPanel = React . forwardRef ( ( props , ref ) => {
35
- const containerRef = useRef ( ) ;
36
-
37
- // We don't want to generate random text every time this component renders
38
- const randomText = useMemo ( ( ) => generateRandomText ( ) , [ ] ) ;
39
-
40
- useImperativeMethods ( ref , ( ) => ( {
41
- scrollIntoView : ( ) => {
42
- scrollIntoView ( containerRef . current , { block : 'nearest' , scrollMode : 'if-needed' } ) ;
43
- }
44
- } ) ) ;
45
-
46
- return < div onClick = { props . onClick } ref = { containerRef } >
47
- < div className = "accordion-label" > { props . label } </ div >
48
- { props . isOpen &&
49
- < div > { randomText } </ div > }
50
- </ div > ;
51
- } ) ;
33
+ // We don't want to re-render this component every time it receives new props
34
+ // We also pass second parameter to this function where we implement our own comparison
35
+ const AccordionPanel = React . memo (
36
+ forwardRef ( ( props , ref ) => {
37
+ const containerRef = useRef ( ) ;
38
+
39
+ useImperativeMethods ( ref , ( ) => ( {
40
+ scrollIntoView : ( ) => {
41
+ scrollIntoView ( containerRef . current , { block : 'nearest' , scrollMode : 'if-needed' } ) ;
42
+ }
43
+ } ) ) ;
44
+
45
+ return < div onClick = { props . onClick } ref = { containerRef } >
46
+ < div className = "accordion-label" > { props . label } </ div >
47
+ { props . isOpen &&
48
+ < div > { props . content } </ div > }
49
+ </ div > ;
50
+ } ) ,
51
+ // practically shouldComponentUpdate, but reversed
52
+ ( prevProps , nextProps ) => prevProps . isOpen === nextProps . isOpen
53
+ ) ;
52
54
53
55
function Accordion ( props ) {
54
56
return < div > { props . children } </ div > ;
55
57
}
56
58
57
- function generateRandomNumber ( max ) {
58
- return Math . floor ( Math . random ( ) * Math . floor ( max ) ) ;
59
- }
60
-
61
- function generateRandomText ( ) {
62
- return new FoobarIpsum ( {
63
- size : {
64
- sentence : generateRandomNumber ( 100 ) ,
65
- paragraph : generateRandomNumber ( 10 )
66
- }
67
- } ) . paragraph ( ) ;
68
- }
69
-
70
59
export {
71
60
useAccordion ,
72
61
Accordion ,
0 commit comments