Dead
Dead

Reputation: 1183

How to update array of a component from another component's function?

I am rebuilding a static website in react https://movie-list-website-wt.netlify.app/ I am trying to transfer the search function to react. My current search function works as intended, it returns an array of movies that is being searched, I want it to update the data fetched by the movie cards so that when I search a query, the movie cards use the returned search data instead of the original one this is my App.js file

import React, { useEffect, useState } from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import Movies from './components/Movies';
import Topnav from './components/Topnav';
import './App.css';
import './components/Movies.css';

export const API_KEY = 'api_key=247b6aa143f3f2c0b100c0cbdfb1ac99';
export const BASE_URL = 'https://api.themoviedb.org/3';
export const API_URL = BASE_URL + '/discover/movie?sort_by=popularity.desc&' + API_KEY;
export const IMG_URL = 'https://image.tmdb.org/t/p/w500';
export const searchURL = BASE_URL + '/search/movie?'+ API_KEY;

function App() {
  const [movies, setMovies] = useState ([]);

  useEffect(() => {
    fetch (API_URL)
      .then(res => res.json())
      .then(data => {
        console.log(data);
        setMovies(data.results);
      });
  }, [])

  return (
    <>
        <Router>
          <Topnav />
          <div className="movie-container">
            {movies.length > 0 && 
              movies.map((movie) => (
                <Movies key={movie.id} {...movie} />))}
          </div>
          <Routes>
            <Route exact path='/' />
          </Routes>
        </Router>
    </>
  );
}

export default App;

and this is part of my Top navigation component that includes the search bar and function

import React, { useState } from 'react';
import { Link } from 'react-router-dom';
import './Topnav.css';
import { searchURL } from '../App';

function Topnav() {
    const [click, setClick] = useState(false)
    const handleClick = () => setClick(!click)

    const modeToggle = () => {
        document.body.classList.toggle('dark')
    }

    const [query, setQuery] = useState("")

    const onChange = (e) => {
        e.preventDefault();
        setQuery(e.target.value)
        fetch(searchURL+`&language=en-US&page=1&include_adult=false&query=${e.target.value}`)
        .then((res) => res.json())
        .then((data) => {
            console.log(data.results);
        })
    }

    return (
        <>
        <nav className="topnav">
            <div className="topnav-container">
                <Link to='/' className='topnav-logo'>
                    <img src={require('../img/logo.png').default} alt="logo" />
                </Link>
                <div className="wrapper">
                    <form id='search-bar'>
                        <input
                        type="text"
                        placeholder="Search"
                        className="search"
                        id="search"
                        value={query}
                        onChange={onChange}
                        />
                    </form>
                    <label className="switch">
                        <input type="checkbox" id="mode-toggle" onChange={modeToggle}/>
                    </label>
                </div>

Upvotes: 1

Views: 319

Answers (2)

Ranny
Ranny

Reputation: 136

It is strait forward just move the method onChange to the parent component and pass it as a props

import React, { useEffect, useState } from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import Movies from './components/Movies';
import Topnav from './components/Topnav';
import './App.css';
import './components/Movies.css';

export const API_KEY = 'api_key=247b6aa143f3f2c0b100c0cbdfb1ac99';
export const BASE_URL = 'https://api.themoviedb.org/3';
export const API_URL = BASE_URL + '/discover/movie?sort_by=popularity.desc&' + API_KEY;
export const IMG_URL = 'https://image.tmdb.org/t/p/w500';
export const searchURL = BASE_URL + '/search/movie?'+ API_KEY;

function App() {
const [movies, setMovies] = useState ([]);
const [, setQuery] = useState("")

useEffect(() => {
    fetch (API_URL)
    .then(res => res.json())
    .then(data => {
        console.log(data);
        setMovies(data.results);
    });
}, [])

const onChange = (e) => {
    e.preventDefault();
    setQuery(e.target.value)
    fetch(searchURL+`&language=en-US&page=1&include_adult=false&query=${e.target.value}`)
    .then((res) => res.json())
    .then((data) => {
        console.log(data.results);
        // DO what ever you want with the move array
    })
}

return (
    <>
        <Router>
        <Topnav onChange={onChange} query={query}/>
        <div className="movie-container">
            {movies.length > 0 && 
            movies.map((movie) => (
                <Movies key={movie.id} {...movie} />))}
        </div>
        <Routes>
            <Route exact path='/' />
        </Routes>
        </Router>
    </>
);
}

in this was way can edit the movie array in the APP Componenet then your child component will be something like

import React, { useState } from 'react';
import { Link } from 'react-router-dom';
import './Topnav.css';
import { searchURL } from '../App';

function Topnav({onChange, query}) {
    const [click, setClick] = useState(false)
    const handleClick = () => setClick(!click)

    const modeToggle = () => {
        document.body.classList.toggle('dark')
    }

    return (
        <>
        <nav className="topnav">
            <div className="topnav-container">
                <Link to='/' className='topnav-logo'>
                    <img src={require('../img/logo.png').default} alt="logo" />
                </Link>
                <div className="wrapper">
                    <form id='search-bar'>
                        <input
                        type="text"
                        placeholder="Search"
                        className="search"
                        id="search"
                        value={query}
                        onChange={onChange}
                        />
                    </form>
                    <label className="switch">
                        <input type="checkbox" id="mode-toggle" onChange={modeToggle}/>
                    </label>
                </div>
                ...

Upvotes: 0

lpizzinidev
lpizzinidev

Reputation: 13289

If you need to filter the movies array in the Topnav component you can just pass the setMovies state function as prop to the component.
All you need to do is update the data in the onChange method:

App.js

  return (
    <>
        <Router>
          <!-- Pass the prop to the component -->
          <Topnav setMovies={setMovies}/>
          <div className="movie-container">
            {movies.length > 0 && 
              movies.map((movie) => (
                <Movies key={movie.id} {...movie} />))}
          </div>
          <Routes>
            <Route exact path='/' />
          </Routes>
        </Router>
    </>
  );

Topnav.js

function Topnav({ setMovies }) {

    ...

    const onChange = (e) => {
        e.preventDefault();
        setQuery(e.target.value)
        fetch(searchURL+`&language=en-US&page=1&include_adult=false&query=${e.target.value}`)
        .then((res) => res.json())
        .then((data) => {
            console.log(data.results);
            // Update the data once fetched
            setMovies(data.results)
        })
    }

    ...

Upvotes: 2

Related Questions