3

My parent component handles requests to the server and provides its child-components with data from the response. One child-component (FilterSidebar: contains filter components) should in that case only be rendered once, because the filters will be created from the initial requested data.

This is why I want to use React's memo hook. The according data is only passed once to the filter-component. The log inside the areEqual function is never called, and the component will always render again.

Parent component:

import React, { useState, useRef } from "react";
import { useQuery } from '@apollo/react-hooks';
import { REQUEST_INQUIRIES } from './requests/REQUEST_INQURIES.js'
import Canvas from './components/Canvas.js';
import FilterSidebar from './components/filter/FilterSidebar.js';
import Loader from './components/Loader.js'
import QueryContext from './components/filter/query-context.js'

const App = () => {

    const [query, setQuery] = useState(["{\"name\": {\"$LIKE\": \"%\"}}"])

    // used to create filters once with initial data
    const [keys, setKeys] = useState([])
    const [values, setValues] = useState([])
    const ref = useRef(false);

    const { loading, data, error } = useQuery(REQUEST_INQUIRIES, {
        variables: { filters: `{ \"$AND\": [ ${query.map(q => { return q})} ]}` }
    })

    const addQuery = (queryPart) => {
        if (!query.includes(queryPart)) setQuery(prevState => [...prevState, queryPart])
    }

    const contextValue = { query, addQuery }

    // return loading animation here
    if (loading) { return <Loader /> }

    // return some kind of error message here
    if (error) { return <p>Error...</p> }

    const { edges } = data.getHMCInquiryListing
    
    if (!ref.current) {
        // will only be called once

        // some data transformation ...

        setKeys(tmpKeys)
        setValues(tmpValues)
        ref.current = true;
    }

    return (
        <QueryContext.Provider value={contextValue}>
            <FilterSidebar keys={keys} values={values} />
            <Canvas data={edges} />
        </QueryContext.Provider>
    )


}

export default App;

Child component:

import React, { memo } from "react"
import GenericFilter from './GenericFilter.js'
import SpecificFilter from './SpecificFilter.js'

const FilterSidebar = (props) => {

    const {keys, values} = props

    return (
        <div className='filter-sidebar'>
            <div className="filters">
                <GenericFilter attributes={keys} />
                {keys.map(attribute => {                        
                    return <SpecificFilter key={attribute} attribute={attribute} values={values[attribute]} />
                })}
            </div>
        </div>
    );

}

const areEqual = (prevProps, nextProps) => {
    console.log(prevProps, nextProps)
}

export default memo(FilterSidebar, areEqual)

Thanks in advance.

Gh05d
  • 7,923
  • 7
  • 33
  • 64
philman
  • 129
  • 2
  • 10

1 Answers1

0

memo works only in re renders, since you are passing key which is a state variable it will no re-render rather create a new reference of the component and it will never compare prev props with new props as it considers it to be a completely new component. You can read about how React uses keys as one of the factors to decide when to re-render a component in their documentation.