#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;
Комментарии:
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>