Я создаю мини-приложение и хочу сделать его чище.
Итак, в основном я хочу иметь 3 компонента: App, List и Person.
Вот код: App.js
import React, { Component } from "react";
import List from './List';
class App extends Component {
constructor(props) {
super(props);
this.state = {
results: [],
search: '',
currentPage: 1,
todosPerPage: 3
};
this.handleClick = this.handleClick.bind(this);
}
componentWillMount() {
this.fetchData();
}
fetchData = async () => {
const response = await fetch(API);
const json = await response.json();
this.setState({ results: json.results });
};
handleClick(event) {
this.setState({
currentPage: Number(event.target.id)
});
}
updateSearch(event) {
this.setState({ search: event.target.value.substr(0, 20) });
}
render() {
return (
<List />
);
}
}
export default App;
List.js
import React, { Component } from 'react';
import Person from './Person';
class List extends Component {
render() {
const { results, currentPage, todosPerPage } = this.state;
const indexOfLastTodo = currentPage * todosPerPage;
const indexOfFirstTodo = indexOfLastTodo - todosPerPage;
const currentTodos = results.slice(indexOfFirstTodo, indexOfLastTodo).filter(item => {
return item.name.toLowerCase().indexOf(this.state.search) !== -1;
});
const renderTodos = currentTodos.map((item, index) => {
return (
<Person item = {this.state.item} index = {this.state.index}/>
);
});
const pageNumbers = [];
for (let i = 1; i <= Math.ceil(results.length / todosPerPage); i++) {
pageNumbers.push(i);
}
const renderPageNumbers = pageNumbers.map(number => {
return (
<li className = "page-link" key = {number} id = {number} onClick = {this.handleClick} style = {{cursor: "pointer"}}>{number}</li>
);
});
return (
<div className = "flex-grow-1">
<h1>Personnages de Star Wars</h1>
<form className = "mb-4">
<div className = "form-group">
<label>Rechercher</label>
<input
className = "form-control"
type = "text"
placeholder = "luke skywalker..."
value = {this.state.search}
onChange = {this.updateSearch.bind(this)}
/>
</div>
</form>
<div className = "row mb-5">{renderTodos}</div>
<nav aria-label = "Navigation">
<ul id = "page-number" className = "pagination justify-content-center">{renderPageNumbers}</ul>
</nav>
</div>
);
}
}
export default List;
Person.js
import React, { Component } from 'react';
function Person(item, index) {
return (
<div className = "col-lg-4 mb-4" key = {index}>
<div className = "card">
<div className = "card-header">
<h4 className = "mb-0">{item.name}</h4>
</div>
<div className = "card-body">
<h5 className = "card-title">Caractéristiques</h5>
<ul>
<li>Année de naissance : {item.birth_year}</li>
<li>Taille : {item.height} cm</li>
<li>Masse : {item.mass}</li>
<li>Couleur des yeux : {item.eye_color}</li>
<li>Couleur de cheveux : {item.hair_color}</li>
<li>Couleur de peau : {item.skin_color}</li>
</ul>
<a href = {item.url} className = "btn btn-primary">Sa fiche</a>
</div>
</div>
</div>
)
}
export default Person;
Моя проблема в том, что я получаю TypeError: Cannot read property 'results' of null при рендеринге.
Возможно ли, чтобы переменная попадала в каждый файл, если я определяю их все в App.js?





Вы можете использовать переменные передачи в реквизитах компонента <List />, передав состояние внутри функции рендеринга App.js при вызове <List /> следующим образом.
render() {
//Passing Data inside props
<List data = {this.state} />
}
и внутри вашего List.js вы можете получить доступ к переменной данных
const { results, currentPage, todosPerPage } = this.props.data;
Вам нужно передать элемент внутри реквизита данных внутри компонента Person, и вам нужно передать индекс в качестве другого реквизита, подобного этому <Person data = {item} index = {index} />, и вы можете получить доступ к подобному индексу внутри компонента Person this.props.index, и вы можете получить доступ к данным the.props.data.
Вы не правильно передаете данные. Попробуй это:
В App.js передайте компоненту List необходимые данные:
render() {
return (
<List data = {this.state}/>
);
}
Затем в методе render() в List.js получите переданную опору данных, а затем извлеките оттуда данные:
render() {
const { data } = this.props;
const { results, search, currentPage, todosPerPage } = data;
// ...
// in currentTodos function dont use this.state.search but just "search", that we got above from the data variable
// ...
// also your renderTodos should look like this - use the item and index variables
const renderTodos = currentTodos.map((item, index) => {
return (
<Person item = {item} index = {index}/>
);
});
// ...
}
Итак, ваш List.js должен выглядеть так:
import React, { Component } from 'react';
import Person from './Person';
class List extends Component {
render() {
// get the data
const { data } = this.props;
// get the properties
const { results, search, currentPage, todosPerPage } = data;
const indexOfLastTodo = currentPage * todosPerPage;
const indexOfFirstTodo = indexOfLastTodo - todosPerPage;
const currentTodos = results.slice(indexOfFirstTodo, indexOfLastTodo).filter(item => {
// use "search" variable
return item.name.toLowerCase().indexOf(search) !== -1;
});
const renderTodos = currentTodos.map((item, index) => {
return (
// use item and index
<Person item = {item} index = {index}/>
);
});
const pageNumbers = [];
for (let i = 1; i <= Math.ceil(results.length / todosPerPage); i++) {
pageNumbers.push(i);
}
const renderPageNumbers = pageNumbers.map(number => {
return (
<li className = "page-link" key = {number} id = {number} onClick = {this.handleClick} style = {{cursor: "pointer"}}>{number}</li>
);
});
return (
<div className = "flex-grow-1">
<h1>Personnages de Star Wars</h1>
<form className = "mb-4">
<div className = "form-group">
<label>Rechercher</label>
<input
className = "form-control"
type = "text"
placeholder = "luke skywalker..."
value = {search} // use search variable here too
onChange = {this.updateSearch.bind(this)}
/>
</div>
</form>
<div className = "row mb-5">{renderTodos}</div>
<nav aria-label = "Navigation">
<ul id = "page-number" className = "pagination justify-content-center">{renderPageNumbers}</ul>
</nav>
</div>
);
}
}
export default List;
И ваша функция в Person.js должна иметь следующее объявление, потому что параметры извлекаются из переданных реквизитов:
function Person({item, index}) {
// ...
}
Отлично, я понимаю эту часть! Как это работает с .map (внутри
<List />)? Когда я позвоню<Person />const renderTodos = currentTodos.map((item, index) => { return ( <Person data = {this.state} /> ); });иclass Person extends Component { render() { const results = this.props.data; return ( <div className = "col-lg-4 mb-4" key = {results.index}>поймайте меняTypeError: Cannot read property 'index' of null