Отображать данные из «this.state.data»?

#javascript #reactjs

Вопрос:

Цель:
*Получить данные переменных автомобилей в «this.state.data», когда вы извлекли данные из API.
*Отобразить данные из «this.state.data», не используя переменные автомобили.

Проблема:
Я не знаю, как это сделать, и возможно ли это сделать, когда вы применили рефакторинг SOLID?

Информация:
Я новичок в React JS.

Стакблитц:
https://stackblitz.com/edit/react-v39jre?


App.js

 import React from 'react';
import './style.css';
import CarsList from './components/CarsList';
import React, { Component } from 'react';

class App extends Component {
  constructor() {
    super();
    this.state = {
      name: 'React',
      data: null
    };
  }

  render() {
    return (
      <div className="App">
        <CarsList />
      </div>
    );
  }
}

export default App;
 

Список автомобилей.jsx

 import React, { useState, useEffect } from 'react';

this.state = {
  name: 'React',
  data: null
};

const CarsList = () => {
  const [cars, setCars] = useState([]);
  useEffect(() => {
    const fetchCars = async () => {
      const response = await fetch(
        'https://jsonplaceholder.typicode.com/users'
      );
      setCars(await response.json());
    };
    fetchCars();
  }, []);
  return (
    <div>
      {cars.map((car, index) => (
        <li key={index}>
          [{  index}]{car.id} - {car.name}$
        </li>
      ))}
    </div>
  );
};

export default CarsList;
 

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

1. Итак, чего вы пытаетесь достичь, чтобы вызвать api — получить данные и отобразить их в виде структуры ul > li в части визуализации ? Это так??

2. Я хочу, чтобы эти данные находились по адресу App.js и не только в списке автомобилей. Я хочу повторно использовать данные в другом компоненте, например CarsList.2jsx

3. Поэтому вам нужно выполнить функцию обратного вызова от дочернего элемента к родительскому, но вместо вызова этого api в компоненте CarsList, почему бы вам не вызвать его в родительском компоненте. это в основном называется отменой государственного подхода ?

4. Вы вообще собираетесь использовать крючки? Потому что вопрос задается о компоненте React, который их не использует, но carsList является функциональным компонентом, который их использует.

5. «Таким образом, вам нужно выполнить функцию обратного вызова от ребенка к родителю, но вместо вызова этого api в компоненте CarsList, почему бы вам не вызвать его в родительском компоненте. это в основном называется» подход к состоянию», речь идет о рефакторинге. В этом примере у вас есть только CarsList.jsx, но на самом деле у вас может быть 6 разных CarsList.jsx.

Ответ №1:

После получения ответа в дочернем компоненте вы должны выполнить функцию обратного вызова, которая может быть передана как поддержка от родителя к ребенку. С помощью этой функции вы можете передавать данные от ребенка к родителю и обновлять родительское состояние.

App.js

 import { useState } from "react";
import CarsList from "./CarsList";
import "./styles.css";

export default function App() {
  const [state, setState] = useState([]);

  const handleUpdateParentState = (data) => {
    setState(data);
  };

  console.log("state in parent", state);

  return (
    <div>
      <CarsList updateParentState={handleUpdateParentState} />
    </div>
  );
}
 

CarsList.js

 import React, { useState, useEffect } from "react";

const CarsList = (props) => {
  const [cars, setCars] = useState([]);

  useEffect(() => {
    const fetchCars = async () => {
      try {
        const response = await fetch(
          "https://jsonplaceholder.typicode.com/users"
        );
        const data = await response.json();
        setCars(data);
        props?.updateParentState(data);
      } catch (error) {
        console.log(error);
      }
    };
    fetchCars();
  }, []);
  return (
    <ul>
      {cars?.map((car, index) => (
        <li key={index}>
          [{  index}]{car.id} - {car.name}$
        </li>
      ))}
    </ul>
  );
};

export default CarsList;
 

Codesandbox

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

1. setState(data); не будет работать, если каждый список автомобилей передает свои данные в компонент приложения. Это должно было быть что-то вроде setState(prev => ({ ...prev, data })); . И вопрос предполагает, что операция предназначена для использования компонентов реакции, а не функциональных компонентов.

Ответ №2:

Данные могут совместно использоваться с помощью реквизитов, но только от родительского компонента к дочернему компоненту. Мы не можем передать состояние дочернего компонента родительскому компоненту с помощью реквизитов.

Хотя мы можем создать функцию на родительском уровне и передать ее дочернему компоненту в качестве реквизита, чтобы мы могли там выполнять.

В вашем случае вам нужно создать функцию в App компоненте и передать ее carList компоненту в качестве реквизита. В carList компоненте вам не нужно создавать cars состояние. После извлечения автомобилей из API просто вызовите функцию, которую вы передали из App компонента

App.js

 import React from 'react';
import './style.css';
import CarsList from './components/CarsList';
import React, { Component } from 'react';

class App extends Component {
  constructor() {
    super();
    this.state = {
      name: 'React',
      data: null
    };
  }

  function setCarList(cars) {
    this.setState({
      date: cars
    });
  }

  render() {
    return (
      <div className="App">
        <CarsList setCars={setCarList}/>
      </div>
    );
  }
}

export default App;
 

CarList.js

 import React, {useEffect } from 'react';

this.state = {
  name: 'React',
  data: null
};

const CarsList = (props) => {
  useEffect(() => {
    const fetchCars = async () => {
      const response = await fetch(
        'https://jsonplaceholder.typicode.com/users'
      );
      this.props.setCars(await response.json());
    };
    fetchCars();
  }, []);
  return (
    <div>
      {cars.map((car, index) => (
        <li key={index}>
          [{  index}]{car.id} - {car.name}$
        </li>
      ))}
    </div>
  );
};

export default CarsList;
 

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

1. <CarsList setCars="setCarList"/> должно быть <CarsList setCars={setCarList} /> .

Ответ №3:

Для каждого компонента нет особого смысла CarList загружать данные, если у вас их будет много, и они будут обмениваться информацией друг с другом. Вы должны загрузить все свои данные в свой App компонент, используя массив fetch вызовов API, а затем использовать Promise.all для извлечения и анализа данных, а затем добавить их в состояние. Это состояние затем может быть передано всем вашим Carlist компонентам.

Вот компонент реакции:

 const {Component} = React;

const json = '["BMW", "Clio", "Merc", "Fiat"]';

// Simulates an API call
function mockFetch() {
  return new Promise((res, rej) => {
    setTimeout(() => res(json), 1000);
  });
}

class App extends Component {

  constructor() {
    super();
    this.state = { cars: [] };
  }

  componentDidMount() {

    // Have an array fetches (you would supply each one a
    // different API endpoint in your code)
    const arr = [mockFetch(), mockFetch(), mockFetch()];

    // Grab the json, `map` over it and parse it
    Promise.all(arr).then(data => {
      const cars = data.map(arr => JSON.parse(arr));

      // Then set the new state
      this.setState(prev => ({ ...prev, cars }));
    });
  }

  // You can now send the data to your small functional
  // carlist components
  render() {
    const { cars } = this.state;
    if (!cars.length) return <div />;
    return (
      <div>
        <Carlist cars={cars[0]} />
        <Carlist cars={cars[1]} />
        <Carlist cars={cars[2]} />
      </div>
    )
  }

};

function Carlist({ cars }) {
  return (
    <ul>{cars.map(car => <div>{car}</div>)}</ul>
  );
}

// Render it
ReactDOM.render(
  <App />,
  document.getElementById("react")
); 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="react"></div> 

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

 const {useState, useEffect} = React;

const json = '["BMW", "Clio", "Merc", "Fiat"]';

function mockFetch() {
  return new Promise((res, rej) => {
    setTimeout(() => res(json), 1000);
  });
}

function App() {

  const [cars, setCars] = useState([]);

  // This works in the same way as the previous example
  // except we're not setting `this.state` we're setting the
  // state called `cars` that we set up with `useState`.
  useEffect(() => {
    function getData() {
      const arr = [mockFetch(), mockFetch(), mockFetch()];
      Promise.all(arr).then(data => {
        const cars = data.map(arr => JSON.parse(arr));
        setCars(cars);
      });
    }
    getData();
  }, []);

  if (!cars.length) return <div />;

  return (
    <div>
      <Carlist cars={cars[0]} />
      <Carlist cars={cars[1]} />
      <Carlist cars={cars[2]} />
    </div>
  );
};

function Carlist({ cars }) {
  return (
    <ul>{cars.map(car => <div>{car}</div>)}</ul>
  );
}

// Render it
ReactDOM.render(
  <App />,
  document.getElementById("react")
); 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="react"></div>