Как обновить массив компонента из функции другого компонента?

#javascript #html #reactjs

Вопрос:

Я перестраиваю статический веб — сайт в react https://movie-list-website-wt.netlify.app/ Я пытаюсь передать функцию поиска для реагирования. Моя текущая функция поиска работает по назначению, она возвращает массив фильмов, в которых выполняется поиск, я хочу, чтобы она обновила данные, полученные с помощью карточек фильмов, чтобы при поиске по запросу карточки фильмов использовали возвращенные данные поиска вместо исходных. Это мой App.js файл

 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.descamp;'   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 amp;amp; 
              movies.map((movie) => (
                <Movies key={movie.id} {...movie} />))}
          </div>
          <Routes>
            <Route exact path='/' />
          </Routes>
        </Router>
    </>
  );
}

export default App;
 

и это часть моего главного навигационного компонента, который включает в себя панель поиска и функцию

 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 `amp;language=en-USamp;page=1amp;include_adult=falseamp;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>
 

Ответ №1:

Если вам нужно отфильтровать movies массив в Topnav компоненте, вы можете просто передать компоненту setMovies функцию состояния как prop.
Все, что вам нужно сделать, это обновить данные в onChange методе:

App.js

   return (
    <>
        <Router>
          <!-- Pass the prop to the component -->
          <Topnav setMovies={setMovies}/>
          <div className="movie-container">
            {movies.length > 0 amp;amp; 
              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 `amp;language=en-USamp;page=1amp;include_adult=falseamp;query=${e.target.value}`)
        .then((res) => res.json())
        .then((data) => {
            console.log(data.results);
            // Update the data once fetched
            setMovies(data.results)
        })
    }

    ...
 

Комментарии:

1. Это сработало как заклинание, большое вам спасибо, реагировать-это довольно сложная задача для меня, прыгнуть в нее

Ответ №2:

Это прямолинейно, просто переместите метод onChange в родительский компонент и передайте его в качестве реквизита

 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.descamp;'   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 `amp;language=en-USamp;page=1amp;include_adult=falseamp;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 amp;amp; 
            movies.map((movie) => (
                <Movies key={movie.id} {...movie} />))}
        </div>
        <Routes>
            <Route exact path='/' />
        </Routes>
        </Router>
    </>
);
}
 

таким образом, вы можете редактировать массив фильмов в компоненте ПРИЛОЖЕНИЯ,
тогда ваш дочерний компонент будет выглядеть примерно так

 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>
                ...