Составные компоненты. Поиск в списке

Последнее обновление: 01.04.2022

Компоненты могут состоять из других компонентов. Стандартный пример - какие-нибудь списки, например, меню. Каждый элемент меню может быть представлен отдельным компонентом, а само меню также может представлять компонент. Например:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>METANIT.COM</title>
</head>
<body>
    <div id="app"></div>
      
    <script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/babel">
        class Item extends React.Component {
            render() {
              return <li>{this.props.name}</li>;
            }
          }
            
          class ItemsList extends React.Component {
            render() {
              return(
              <div>         
                  <h2>{this.props.title}</h2>
                  <ul>
                      <Item name="Apple iPhone 12 Pro" />
                      <Item name="Samsung Galaxy Note20" />
                      <Item name="Google Pixel 5" />
                  </ul>
              </div>);
            }
          }
            
		ReactDOM.createRoot(
            document.getElementById("app")
        )
        .render(
           <ItemsList title="Список смартфонов" />
       );
    </script>
</body>
</html>
Составные компоненты в React

В принципе мы могли бы определить все в одном компоненте, однако выделение отдельного компонента Item позволяет развивать и обновлять его разметку отдельно от родителького компонента. Например, если потребуется изменить структуру разметки html в компоненте, то достаточно это сделать в коде класса Item. К тому же мы можем повторно использовать Item в других компонентах.

В то же время в примере выше есть один минус - данные жестко закодированы в элементе Item и смешаны с представлением. Второй момент, который надо отметить, - нередко данные передаются от родительского компонента к дочернему, особенно в списках. Поэтому изменим код JSX в примере выше следующим образом:

const propsValues = {
	title: "Список смартфонов",
	items: [
			"Samsung Galaxy Note20", 
            "Apple iPhone 12 Pro", 
            "Google Pixel 5", 
            "Huawei P40 Pro", 
            "OnePlus 8 Pro", 
            "Asus Zenfone 7 Pro"
	]
};
	
class Item extends React.Component {
	render() {
		return <li>{this.props.name}</li>;
	}
}
		
class ItemsList extends React.Component {
	render() {
		return(
			<div>			
				<h2>{this.props.data.title}</h2>
				<ul>
				{
					this.props.data.items.map(function(item){
						return <Item key={item} name={item} />
					})
				}
				</ul>
			</div>);
	}
}
ReactDOM.createRoot(
	document.getElementById("app")
)
.render(
    <ItemsList data={propsValues} />
);

Здесь отделены данные от представления. Из главного компонента ItemsList в дочерний компонент Item данные передаются через значение this.props.data.items.

Передача данных между компонентами в React

Теперь пойдем дальше и добавим к списку поиск:

const propsValues = {
	title: "Список смартфонов",
	items: [
			"Samsung Galaxy Note20", 
            "Apple iPhone 12 Pro", 
            "Google Pixel 5", 
            "Huawei P40 Pro", 
            "OnePlus 8 Pro", 
            "Asus Zenfone 7 Pro"
	]
};
	
class Item extends React.Component {
	render() {
		return <li>{this.props.name}</li>;
	}
}
		
class ItemsList extends React.Component {
	constructor(props){
		super(props);
		this.state = { items: this.props.data.items};
				
		this.filterList = this.filterList.bind(this);
	}
	// фильтрация списка
	filterList(e){
		var filteredList = this.props.data.items.filter(function(item){
			return item.toLowerCase().search(e.target.value.toLowerCase())!== -1;
		});
		// обновление состояния
		this.setState({items: filteredList});
	}
	
	render() {
		return(
			<div>			
				<h2>{this.props.data.title}</h2>
				<input placeholder="Поиск" onChange={this.filterList} />
				<ul>
					{
						this.state.items.map(function(item){
							return <Item key={item} name={item} />
						})
					}
				</ul>
			</div>);
	}
}

ReactDOM.createRoot(
	document.getElementById("app")
)
.render(
    <ItemsList data={propsValues} />
);

Теперь компонент ItemsList выводит в список не те объекты, которые передаются через this.props.data.items, а объекты из состояния this.state.items. В самом начале эти объекты совпадают.

Текстовое поле над списком использует функцию filterList для обработки события change - изменения ввода в текстовое поле. В функции filterList используя начальный список this.props.data.items, фильтруем его и переустанавливаем объекты в this.state.items. В итоге поле ввода будет изменяться состояние компонента ItemsList, что приведет повторному рендерингу компонента.

Фильтрация в списке в React

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

const propsValues = {
	title: "Список смартфонов",
	items: [
			"Samsung Galaxy Note20", 
            "Apple iPhone 12 Pro", 
            "Google Pixel 5", 
            "Huawei P40 Pro", 
            "OnePlus 8 Pro", 
            "Asus Zenfone 7 Pro"
	]
};
			 
class Item extends React.Component {
	render() {
		return <li>{this.props.name}</li>;
	}
}
		
class SearchPlugin extends React.Component{
			
	constructor(props){
		super(props);
		this.onTextChanged = this.onTextChanged.bind(this);
	}
			
	onTextChanged(e){
		var text = e.target.value.trim();	// удаляем пробелы
		this.props.filter(text); // передаем введенный текст в родительский компонент
	}
			
	render() {
		return <input placeholder="Поиск" onChange={this.onTextChanged} />;
	}
}
				 
class ItemsList extends React.Component {
	constructor(props){
		super(props);
		this.state = { items: this.props.data.items};
						 
		this.filterList = this.filterList.bind(this);
	}
			
	filterList(text){
		var filteredList = this.props.data.items.filter(function(item){
			return item.toLowerCase().search(text.toLowerCase())!== -1;
		});	
		this.setState({items: filteredList});
	}
			 
	render() {
		return(
			<div>         
				<h2>{this.props.data.title}</h2>
				<SearchPlugin filter={this.filterList} />
				<ul>
					{
						this.state.items.map(function(item){
							return <Item key={item} name={item} />
						})
					}
				</ul>
			</div>);
	}
}
		 
ReactDOM.createRoot(
	document.getElementById("app")
)
.render(
    <ItemsList data={propsValues} />
);

Таким образом, приложение для пользователя никак не изменилось - также по ввода данных в поле происходит фильтрация. Но теперь функционал ввода вынесен в отдельный компонент. В нем мы можем как угодно обрабатывать введенное значение, даже изменять его, проверять его значение на предмет некорретных слов или символов. Главному компоненту не важна вся эта предобработка, ему нужен только текст, по которому надо фильтровать.

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850