Search code examples
javascriptreactjsapollo

How to pass callback function parameters to parent react component?


Just wondering the best way to pass the letterSelected into the useLazyQuery fetchMovies query, so that I don't have to use the static variable of "A". I was hoping there was a way to pass it directly into fetchMovies. useLazyQuery is an apollo query.

const BrowseMovies = () => {

const [fetchMovies, { data, loading}] = useLazyQuery(BROWSE_MOVIES_BY_LETTER, {
    variables: {
        firstLetter: "A"
    }
})

return (
    <div className="browserWrapper">
        <h2>Browse Movies</h2>
        <AlphabetSelect 
            pushLetterToParent={fetchMovies}
        />
        {
            data && !loading &&
            data.browseMovies.map((movie: any) => {
                return (
                    <div className="browseRow">
                        <a className="movieTitle">
                            {movie.name}
                        </a>
                    </div>
                )
            })
        }
    </div>
)
}

export default BrowseMovies

const AlphabetSelect = ({pushLetterToParent}: any) => {

const letters = ['A','B','C','D','E','F','G','H','I','J','K','L', 'M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','#']

const [selectedLetter, setSelectedLetter] = useState("A")

const onLetterSelect = (letter: string) => {
    setSelectedLetter(letter.toUpperCase())
    pushLetterToParent(letter.toUpperCase())
}

return (
    <div className="alphabetSelect">
        {
            letters.map((letter: string) => {
                return(
                    <div 
                        className={selectedLetter === letter ? 'letterSelectActive' : 'letterSelect'} 
                        onClick={() => onLetterSelect(letter)}
                    >
                        {letter}
                    </div>
                )
            })
        }
    </div>
)
}
export default AlphabetSelect

Solution

  • This appears to be a problem solved by Lifting State Up. useLazyQuery takes a gql query and options and returns a function to execute the query at a later time. Sounds like you want the child component to update the variables config parameter.

    BrowseMovies

    1. Move firstLetter state BrowseMovies component

    2. Update query parameters/options/config from state

    3. Add useEffect to trigger fetch when state updates

    4. Pass firstLetter state and setFirstLetter state updater to child component

      const BrowseMovies = () => {
        const [firstLetter, setFirstLetter] = useState('');
      
        const [fetchMovies, { data, loading}] = useLazyQuery(
          BROWSE_MOVIES_BY_LETTER,
          { variables: { firstLetter } } // <-- pass firstLetter state
        );
      
        useEffect(() => {
          if (firstLetter) {
            fetchMovies(); // <-- invoke fetch on state update
          }
        }, [firstLetter]);
      
        return (
          <div className="browserWrapper">
            <h2>Browse Movies</h2>
            <AlphabetSelect 
              pushLetterToParent={setFirstLetter} // <-- pass state updater
              selectedLetter={firstLetter} // <-- pass state
            />
            {
              data && !loading &&
                data.browseMovies.map((movie: any, index: number) => {
                  return (
                    <div key={index} className="browseRow">
                      <a className="movieTitle">
                        {movie.name}
                      </a>
                    </div>
                  )
                })
            }
          </div>
        )
      }
      

    AlphabetSelect

    1. Attach pushLetterToParent callback to div's onClick handler

       const AlphabetSelect = ({ pushLetterToParent, selectedLetter }: any) => {
      
         const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ#';
      
         return (
           <div className="alphabetSelect">
             {
               letters.split('').map((letter: string) => {
                 return(
                   <div
                     key={letter}
                     className={selectedLetter === letter ? 'letterSelectActive' : 'letterSelect'} 
                     onClick={() => pushLetterToParent(letter.toUpperCase())}
                   >
                     {letter}
                   </div>
                 )
               })
             }
           </div>
         )
       }