使用 React Hooks 创建宝可梦图鉴
大家好,我写这篇指南的目的是为了展示如何使用 React 开发一个简单的应用程序,并手动配置 Webpack 和 Babel。如果您是这些技术的初学者,或者计划在不久的将来使用它们,我希望这篇指南能对您有所帮助。
开始之前
开始编写代码之前,我们需要在计算机上安装一些软件:
就这样,现在我们可以开始开发这个应用程序了。
让我们开始吧
首先,我们需要初始化应用程序。为此,我们需要使用终端并切换到要创建应用程序的文件夹(可以使用命令 `\k` cd [directory])。进入该目录后,我们需要在控制台中输入以下命令:
npm init
此命令将打开一个提示框,要求您进行一些应用程序的初始配置,以便生成 package.json 文件:
填写完信息并输入“yes”保存后,我们应该能够打开 VS Code 或我们选择的代码编辑器。打开文件夹后,我们需要在编辑器中打开嵌入式终端,然后从那里继续安装依赖项。
安装依赖项
首先我们需要安装应用程序依赖项,这里我们将使用 react、react-dom 和 axios,所以我们需要在终端中输入以下命令:
npm i react react-dom axios
接下来我们应该安装开发依赖项,这些依赖项只是为了能够拥有一个开发环境,以便测试应用程序、添加库、检查错误以及在本地主机环境中运行应用程序。
对于这个应用,我们将使用 webpack 和 babel 来生成 bundle,所以我们将在控制台中运行以下命令,将它们安装为开发依赖项:
npm i @babel/core@^7.12.3 babel-loader@^8.1.0 babel-preset-react-app@^7.0.2 css-loader@^5.0.0 html-webpack-plugin@^4.5.0 style-loader@^2.0.0 webpack@^4.44.2 webpack-cli@^3.3.12 webpack-dev-server@^3.11.0 --save-dev
* In this case I specify the library version to avoid problems when we will start configuring webpack and babel
安装完所有依赖项后,package.json 文件应如下所示:
{
"name": "pokeapp",
"version": "1.0.0",
"description": "demo app",
"main": "index.js",
"scripts": {
"start": "webpack-dev-server"
},
"author": "PHD",
"license": "ISC",
"dependencies": {
"axios": "^0.20.0",
"react": "^17.0.1",
"react-dom": "^17.0.1"
},
"devDependencies": {
"@babel/core": "^7.12.3",
"babel-loader": "^8.1.0",
"babel-preset-react-app": "^7.0.2",
"css-loader": "^5.0.0",
"html-webpack-plugin": "^4.5.0",
"style-loader": "^2.0.0",
"webpack": "^4.44.2",
"webpack-cli": "^3.3.12",
"webpack-dev-server": "^3.11.0"
},
"babel": {
"presets": [
"babel-preset-react-app"
]
}
}
* We added some babel configuration to transpile the react app correctly.
"babel": {
"presets": [
"babel-preset-react-app"
]
}
* Also we add in the scripts section the command a script start the app when we'll finish the first configurations.
"scripts": {
"start": "webpack-dev-server"
},
配置 webpack
现在我们已经准备好了依赖项,下一步是设置 webpack,为此我们需要在根文件夹中添加一个 webpack.config.js 文件,这一步只是为了更好地控制构建应用程序时发生的情况。
Webpack 需要一个选项列表来根据该配置生成打包文件,因此我们需要按以下方式导出这些选项:
const webpack = require("webpack");
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
process.env.NODE_ENV = "development";
module.exports = {
... // webpack options goes here
};
要正确配置 webpack,我们需要在 exports 部分设置以下选项:
-
模式。此设置用于启用 webpack 内置的、与每个环境(开发或生产)相对应的优化。
mode: "development" -
目标。在此选项中,我们可以选择部署是在服务器上还是在浏览器上,我们还可以进行更多配置,例如设置多个目标,但这超出了本指南的范围。
target: "web" -
devtool。通过此选项,我们可以控制是否生成源映射以及使用哪种类型的源映射。源映射使我们能够在浏览器中轻松调试已编译的代码。
devtool: "cheap-module-source-map" -
入口点。此设置允许我们定义应用程序的入口点。
entry: "./src/index" -
输出。此键指示 webpack 应如何以及在哪里输出打包文件和资源。
output: { path: path.resolve(__dirname, "build"), publicPath: "/", filename: "pokebundle.js", } -
devServer。在本指南中,我们将使用 devServer 来开发应用程序,此选项允许我们配置此服务器在本地主机上的运行方式。
devServer: { open: true, stats: "minimal", overlay: true, historyApiFallback: true, disableHostCheck: true, headers: { "Access-Control-Allow-Origin": "*" }, https: false, } -
插件。此键用于配置 webpack 插件,这些插件可以帮助我们在打包应用程序时执行额外的操作,在本例中,我们将使用 HtmlWebpackPlugin 将一些 html 文件随应用程序一起打包提供。
plugins: [ new HtmlWebpackPlugin({ template: "src/index.html", }), ] -
模块。此选项决定 webpack 如何处理应用程序的不同模块。
module: { rules: [ { test: /\.(js|jsx)$/, exclude: /node_modules/, use: ["babel-loader"], }, { test: /(\.css)$/, use: ["style-loader", "css-loader"], }, ], }
完整的 webpack.config.js 文件应该如下所示:
const webpack = require("webpack");
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
process.env.NODE_ENV = "development";
module.exports = {
mode: "development",
target: "web",
devtool: "cheap-module-source-map",
entry: "./src/index",
output: {
path: path.resolve(__dirname, "build"),
publicPath: "/",
filename: "pokebundle.js",
},
devServer: {
open: true,
stats: "minimal",
overlay: true,
historyApiFallback: true,
disableHostCheck: true,
headers: { "Access-Control-Allow-Origin": "*" },
https: false,
},
plugins: [
new HtmlWebpackPlugin({
template: "src/index.html",
}),
],
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: ["babel-loader"],
},
{
test: /(\.css)$/,
use: ["style-loader", "css-loader"],
},
],
},
};
运行该应用程序
我们将使用简单的文件夹结构,只有一个src主文件夹,里面还有两个子文件夹api,components我们将把所有文件都放在这两个子文件夹里:
现在是时候开始编写代码了。
我们需要添加的第一个文件是应用程序的主组件,为此,请转到components文件夹并创建一个名为 App.js 的文件,然后将以下代码放入该文件中:
import React from "react";
function App() {
return (
<div className="container">
Pokedex goes here
</div>
);
}
export default App;
这个 React Hook 返回一个简单的组件,该组件渲染一个包含一些文本的 div 元素。
为了正确渲染此组件,我们需要向应用程序添加入口点。为此,请转到src文件夹并创建 index.js 文件,然后输入以下代码:
import React from "react";
import { render } from "react-dom";
import App from "./components/App";
document.addEventListener("DOMContentLoaded", () => {
render(<App />, document.getElementById("app"));
});
这段代码用于在 html 页面中渲染应用程序,渲染函数在 DOM 内容加载完毕后查找 id 为“app”的元素,然后尝试在那里渲染我们的组件。
但是我们目前还没有任何 html 页面,所以我们需要在src文件夹中添加一个 html 页面作为应用程序的模板,请创建一个名为 index.html 的文件,并将以下内容放入其中:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Pokedex</title>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css"
integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2"
crossorigin="anonymous"
/>
<script
src="https://code.jquery.com/jquery-3.5.1.slim.min.js"
integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj"
crossorigin="anonymous"
></script>
<script
src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/js/bootstrap.bundle.min.js"
integrity="sha384-ho+j7jyWK8fNQe+A12Hb8AhRq26LrZ/JpcUGGOn+Y7RsweNrtN/tE3MoK7ZeZDyx"
crossorigin="anonymous"
></script>
<script
src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"
integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN"
crossorigin="anonymous"
></script>
</head>
<body>
<div id="app"></div>
</body>
</html>
注意我们是如何向 html 头部添加一些样式表的,这样做是为了在应用程序中使用Bootstrap 。
现在我们准备运行该应用程序,只需转到控制台并输入npm start此命令,即可打包应用程序并打开默认浏览器以运行该应用程序。
获取宝可梦信息
为了获取宝可梦信息,我们将使用宝可梦 API。该 API 提供了一些端点,我们可以利用这些端点获取应用程序所需的所有信息,但首先我们需要创建一些文件,以便将应用程序连接到任何 REST API。
将以下文件添加到api文件夹 ApiService.js 和 ApiUtils.js 中,然后将以下代码放入 ApiService.js 文件中:
import axios from "axios"; // this library is to fetch data from REST APIs
import { handleError, handleResponse } from "./ApiUtils";
const httpRequest = (method, url, request, headers) => {
// return a promise
return axios({
method,
url,
data: request,
headers,
})
.then((res) => {
const result = handleResponse(res);
return Promise.resolve(result);
})
.catch((err) => {
return Promise.reject(handleError(err));
});
};
const get = (url, request, headers) => {
let queryString = "";
if (request && Object.keys(request).length > 0) {
queryString += "?";
let len = Object.keys(request).length,
cnt = 0;
// transform the request object in a query string
for (let key in request) {
cnt++;
queryString += `${key}=${request[key].toString()}`;
if (len > cnt) queryString += "&";
}
}
return httpRequest("get", `${url}${queryString}`, request, headers);
};
const deleteRequest = (url, request, headers) => {
return httpRequest("delete", url, request, headers);
};
const post = (url, request, headers) => {
return httpRequest("post", url, request, headers);
};
const put = (url, request, headers) => {
return httpRequest("put", url, request, headers);
};
const patch = (url, request, headers) => {
return httpRequest("patch", url, request, headers);
};
const Api = {
get,
delete: deleteRequest,
post,
put,
patch,
};
export default Api;
在这个文件中,我们用来axios执行 REST 请求,我们使用另外两个函数来处理响应handleResponse,handleError这些方法都是从 ApiUtils.js 文件导入的。此外,我们还向该get方法添加了一些逻辑,以便以一致的方式执行任何 REST 请求。在文件的末尾,我们将所有方法导出到一个 Api 对象中。
我们需要在 ApiUtils.js 文件中编写以下代码,以便正确处理服务器响应:
export function handleResponse(response) {
if (
response.status === 200 ||
response.status === 202 ||
response.statusText === "OK" ||
response.statusText === "Created"
)
return response.data;
if (response.status === 400) {
// So, a server-side validation error occurred.
// Server side validation returns a string error message, so parse as text instead of json.
const error = response.statusText();
throw new Error(error);
}
throw new Error("Network response was not ok.");
}
// In a real app, would likely call an error logging service.
export function handleError(error) {
console.error("API call failed. " + error);
throw error;
}
现在是时候将应用程序连接到宝可梦 API 了,我们需要在api文件夹内创建一个 PokemonService.js 文件,在这个文件中,我们将添加所有获取宝可梦信息的方法。
首先,我们需要将 API 依赖项导入到服务中:
import ApiService from "./ApiService";
然后我们可以定义我们将要使用的三个异步方法:
-
getKantoPokemon。此方法将获取所有关都地区宝可梦的列表,有了这个列表,我们就可以获取所有宝可梦的更多数据。
export const getKantoPokemon = async () => { try { let response = await ApiService.get(`https://pokeapi.co/api/v2/pokemon`, { limit: 151, }); return response.results; } catch (err) { throw err; } }; -
getPokemonData 方法用于获取宝可梦的详细信息,此方法需要一个 URL 来获取宝可梦信息。
export const getPokemonData = async (url) => { try { let response = await ApiService.get(url); return response; } catch (err) { throw err; } }; -
getPokemonKantoData。此方法使用前两个方法,第一个方法用于获取所有关都地区的宝可梦,第二个方法用于获取第一个调用响应中所有宝可梦的详细信息。
export const getPokemonKantoData = async () => { try { //get pokemon list let pokemons = await getKantoPokemon(); //get promises to obtain data for all pokemon in the list let pokemonPromises = pokemons.map((p) => getPokemonData(p.url)); //return all the pokemon data return await Promise.all(pokemonPromises); } catch (err) { throw err; } };
该文件的完整代码如下:
import ApiService from "./ApiService";
export const getKantoPokemon = async () => {
try {
let response = await ApiService.get(`https://pokeapi.co/api/v2/pokemon`, {
limit: 151,
});
return response.results;
} catch (err) {
throw err;
}
};
export const getPokemonData = async (url) => {
try {
let response = await ApiService.get(url);
return response;
} catch (err) {
throw err;
}
};
export const getPokemonKantoData = async () => {
try {
//get pokemon list
let pokemons = await getKantoPokemon();
//get promises to obtain data for all pokemon in the list
let pokemonPromises = pokemons.map((p) => getPokemonData(p.url));
//return all the pokemon data
return await Promise.all(pokemonPromises);
} catch (err) {
throw err;
}
};
创建宝可梦图鉴组件
我们将使用三个组件,需要home在其中创建文件夹components,然后继续创建以下文件:
-
HomeContainer.js 这个组件将作为我们的容器。
-
PokemonList.js 这个组件将显示所有宝可梦的列表。
-
在这个组件中,PokemonDetail.js 将在用户点击列表中的某个元素后显示宝可梦的详细信息。
此外,我们还需要添加一些 CSS 样式,因此为了在一个文件中处理这些样式,我们需要pokemon.css在该文件夹中创建该文件src。
PokemonList 组件
在这个功能组件中,我们需要接收宝可梦列表和选定的宝可梦作为属性,第一个属性用于以友好的方式显示所有宝可梦,第二个属性用于突出显示选定的宝可梦。
首先,我们需要导入我们将要使用的模块:
import React from "react";
import "../../pokemon.css";
接下来我们需要创建功能组件:
function PokemonList({ pokemons, selectPokemon }) {
... // draw Pokemon function goes here
... // return goes here
};
如果pokemons数组属性包含记录,我们将为<li>数组中的每个对象返回一个项,在这个标签中,我们可以正确渲染这些项,以友好的方式显示它们:
const drawPokemon = () => {
return pokemons.map((p, id) => (
<li
key={id}
onClick={() => selectPokemon(p.id)}
className={
p.selected
? "list-group-item d-flex pokemon-item-list selected"
: "list-group-item d-flex pokemon-item-list"
}
>
<img className="col-3" src={p.sprites.front_default} />
<p className="col-4 pokemon-text-list">N.º {p.id}</p>
<p className="col-5 pokemon-text-list">{p.name}</p>
</li>
));
};
在组件的返回值中,我们需要检查pokemonsprop 的长度是否大于 0,因为我们将从服务器获取数据,而当组件渲染到屏幕上时,此 prop 将没有数据:
return <ul className="list-group">{pokemons.length > 0 && drawPokemon()}</ul>;
最后,别忘了导出组件才能使用它:
export default PokemonList;
完整的文件组件应如下所示:
import React from "react";
import "../../pokemon.css";
function PokemonList({ pokemons, selectPokemon }) {
const drawPokemon = () => {
return pokemons.map((p, id) => (
<li
key={id}
onClick={() => selectPokemon(p.id)}
className={
p.selected
? "list-group-item d-flex pokemon-item-list selected" // the selected class is to highlight the Pokemon selected
: "list-group-item d-flex pokemon-item-list"
}
>
<img className="col-3" src={p.sprites.front_default} />
<p className="col-4 pokemon-text-list">N.º {p.id}</p>
<p className="col-5 pokemon-text-list">{p.name}</p>
</li>
));
};
return <ul className="list-group">{pokemons.length > 0 && drawPokemon()}</ul>;
}
export default PokemonList;
宝可梦细节组件
此功能组件将渲染所选宝可梦的详细信息,包括名称、图片、宝可梦类型等。
首先我们需要导入将要使用的库:
import React from "react";
接下来我们需要创建组件主体:
function PokemonDetail({ pokemon }) {
... // getTypeStyleFunction goes here
... // return goes here
}
在这个组件中,我们使用 getTypeStyle 函数,该函数用于获取一些依赖于宝可梦类型的 CSS 样式:
const getTypeStyle = (type) => {
let backgroundColor = "";
switch (type) {
case "grass":
backgroundColor = "#9bcc50";
break;
case "poison":
backgroundColor = "#b97fc9";
break;
case "fire":
backgroundColor = "#fd7d24";
break;
case "flying":
backgroundColor = "#3dc7ef";
break;
case "water":
backgroundColor = "#4592c4";
break;
case "bug":
backgroundColor = "#729f3f";
break;
case "normal":
backgroundColor = "#a4acaf";
break;
case "electric":
backgroundColor = "#eed535";
break;
case "ground":
backgroundColor = "#ab9842";
break;
case "fairy":
backgroundColor = "#fdb9e9";
break;
case "fighting":
backgroundColor = "#d56723";
break;
case "psychic":
backgroundColor = "#f366b9";
break;
case "rock":
backgroundColor = "#a38c21";
break;
case "steel":
backgroundColor = "#9eb7b8";
break;
case "ghost":
backgroundColor = "#7b62a3";
break;
case "ice":
backgroundColor = "#51c4e7";
case "dragon":
backgroundColor = "#f16e57";
default:
backgroundColor = "#000";
break;
}
return { backgroundColor, color: "#FFF", margin: "5px" };
};
然后,在返回结果中,我们渲染一些 html 代码,以友好的方式显示所选的宝可梦:
return (
<div className="pokemon-image-container">
<h1 className="text-center">
N.º {pokemon.id} {pokemon.name}
</h1>
<img
src={`https://pokeres.bastionbot.org/images/pokemon/${pokemon.id}.png`}
className="img-fluid pokemon-image-detail d-block mx-auto"
/>
<div className="pokemon-box-details">
<ul className="list-group list-group-horizontal justify-content-center">
{pokemon.types.length > 0 &&
pokemon.types.map((t, idx) => (
<li
key={idx}
className="list-group-item d-flex pokemon-list-details"
style={getTypeStyle(t.type.name)}
>
{t.type.name}
</li>
))}
</ul>
</div>
</div>
);
最后别忘了导出组件:
export default PokemonDetail;
完整的文件组件应如下所示:
import React from "react";
function PokemonDetail({ pokemon }) {
const getTypeStyle = (type) => {
let backgroundColor = "";
switch (type) {
case "grass":
backgroundColor = "#9bcc50";
break;
case "poison":
backgroundColor = "#b97fc9";
break;
case "fire":
backgroundColor = "#fd7d24";
break;
case "flying":
backgroundColor = "#3dc7ef";
break;
case "water":
backgroundColor = "#4592c4";
break;
case "bug":
backgroundColor = "#729f3f";
break;
case "normal":
backgroundColor = "#a4acaf";
break;
case "electric":
backgroundColor = "#eed535";
break;
case "ground":
backgroundColor = "#ab9842";
break;
case "fairy":
backgroundColor = "#fdb9e9";
break;
case "fighting":
backgroundColor = "#d56723";
break;
case "psychic":
backgroundColor = "#f366b9";
break;
case "rock":
backgroundColor = "#a38c21";
break;
case "steel":
backgroundColor = "#9eb7b8";
break;
case "ghost":
backgroundColor = "#7b62a3";
break;
case "ice":
backgroundColor = "#51c4e7";
case "dragon":
backgroundColor = "#f16e57";
default:
backgroundColor = "#000";
break;
}
return { backgroundColor, color: "#FFF", margin: "5px" };
};
return (
<div className="pokemon-image-container">
<h1 className="text-center">
N.º {pokemon.id} {pokemon.name}
</h1>
<img
src={`https://pokeres.bastionbot.org/images/pokemon/${pokemon.id}.png`}
className="img-fluid pokemon-image-detail d-block mx-auto"
/>
<div className="pokemon-box-details">
<ul className="list-group list-group-horizontal justify-content-center">
{pokemon.types.length > 0 &&
pokemon.types.map((t, idx) => (
<li
key={idx}
className="list-group-item d-flex pokemon-list-details"
style={getTypeStyle(t.type.name)}
>
{t.type.name}
</li>
))}
</ul>
</div>
</div>
);
}
export default PokemonDetail;
HomeContainer 组件
这个函数组件充当容器,因此在这个组件中,我们将导入其他两个组件,我们将访问 API,我们还将使用一些钩子,例如 useEffect 在屏幕加载时获取宝可梦列表,useState 来处理组件的状态,并将该状态作为 props 传递给子组件。
首先,我们需要导入将要使用的库和组件:
import React, { useEffect, useState } from "react";
import PokemonList from "./PokemonList";
import PokemonDetail from "./PokemonDetail";
import { getPokemonKantoData } from "../../api/PokemonService";
接下来我们需要创建组件主体:
function HomeContainer() {
...// state declarations goes here
...// use effect goes here
...// functions goes here
...// return goes here
}
我们需要用到的州如下
- pokeList。用于处理完整的宝可梦列表。
- filteredPokeList。用于处理已筛选的宝可梦列表。
- 筛选器。用于设置要筛选的宝可梦。
- pokemonSelected。用于处理选定的宝可梦。
const [pokeList, setPokeList] = useState([]);
const [filteredPokeList, setFilteredPokeList] = useState([]);
const [filter, setFilter] = useState("");
const [pokemonSelected, setPokemonSelected] = useState(null);
然后我们需要在应用加载时获取宝可梦列表,为此我们需要使用 useEffect 钩子来调用获取信息的 API:
useEffect(async () => {
try {
let pokemons = await getPokemonKantoData();
setFilteredPokeList(pokemons);
setPokeList(pokemons);
} catch (err) {
alert("an error occurs");
console.error(err);
}
}, []);
filteredPokeList为了实现过滤功能,我们可以使用一个函数,根据接收到的值来设置状态:
const filterPokemon = (value) => {
setFilter(value); // set the filter value
setFilteredPokeList(
pokeList.filter((p) => p.name.toLowerCase().includes(value.toLowerCase()))
); // set the pokemons that match with the value
};
为了突出显示选中的宝可梦,并显示宝可梦的详细信息,我们需要创建一个设置pokemonSelected状态的函数:
const handleSelect = (pokemonId) => {
setPokemonSelected(pokeList.filter((p) => p.id === pokemonId)[0]); // set the selected Pokemon to display the details
setFilteredPokeList(
filteredPokeList.map((p) =>
p.id === pokemonId
? { ...p, selected: true }
: { ...p, selected: false }
)
); // filter the list of pokemons to display
};
最后,我们需要返回容器结构以显示应用程序:
return (
<div className="row pokemon-app-container">
<div className="col-6">
{pokemonSelected && <PokemonDetail pokemon={pokemonSelected} />}
</div>
<div className="col-6 pokemon-list-container">
<div style={{ height: "10%" }}>
<div className="form-group">
<label>Search</label>
<input
type="text"
className="form-control"
placeholder="Type to search a pokemon..."
value={filter}
onChange={(event) => {
let { value } = event.target;
filterPokemon(value);
}}
/>
</div>
</div>
<div style={{ height: "90%", overflowY: "auto" }}>
<PokemonList
pokemons={filteredPokeList}
selectPokemon={handleSelect}
/>
</div>
</div>
</div>
);
最后导出组件以便能够使用它:
export default HomeContainer;
该组件的完整代码应如下所示:
import React, { useEffect, useState } from "react";
import PokemonList from "./PokemonList";
import PokemonDetail from "./PokemonDetail";
import { getPokemonKantoData } from "../../api/PokemonService";
function HomeContainer() {
useEffect(async () => {
try {
let pokemons = await getPokemonKantoData();
console.log(pokemons);
setFilteredPokeList(pokemons);
setPokeList(pokemons);
} catch (err) {
alert("an error occurs");
console.error(err);
}
}, []);
const [pokeList, setPokeList] = useState([]);
const [filteredPokeList, setFilteredPokeList] = useState([]);
const [pokemonSelected, setPokemonSelected] = useState(null);
const [filter, setFilter] = useState("");
const handleSelect = (pokemonId) => {
setPokemonSelected(pokeList.filter((p) => p.id === pokemonId)[0]);
setFilteredPokeList(
filteredPokeList.map((p) =>
p.id === pokemonId
? { ...p, selected: true }
: { ...p, selected: false }
)
);
};
const filterPokemon = (value) => {
setFilter(value);
setFilteredPokeList(
pokeList.filter((p) => p.name.toLowerCase().includes(value.toLowerCase()))
);
};
return (
<div className="row pokemon-app-container">
<div className="col-6">
{pokemonSelected && <PokemonDetail pokemon={pokemonSelected} />}
</div>
<div className="col-6 pokemon-list-container">
<div style={{ height: "10%" }}>
<div className="form-group">
<label>Search</label>
<input
type="text"
className="form-control"
placeholder="Type to search a pokemon..."
value={filter}
onChange={(event) => {
let { value } = event.target;
filterPokemon(value);
}}
/>
</div>
</div>
<div style={{ height: "90%", overflowY: "auto" }}>
<PokemonList
pokemons={filteredPokeList}
selectPokemon={handleSelect}
/>
</div>
</div>
</div>
);
}
export default HomeContainer;
宝可梦 CSS 样式表
我不想深入探讨 CSS,因为我认为这超出了本指南的范围,所以我只在这里添加样式表:
.pokemon-item-list {
border-radius: 40px !important;
margin-top: 10px;
margin-bottom: 10px;
border-width: 0px;
}
.pokemon-item-list.selected {
background-color: #e3350d;
color: white;
border-width: 1px;
}
.pokemon-item-list:hover {
border-width: 1px;
background-color: #E2E2E2;
color: white;
}
.pokemon-text-list {
font-size: 24px;
margin-top: 20px;
}
.pokemon-app-container {
height: 100vh;
}
.pokemon-list-container {
height: 100%;
overflow-y: auto;
}
.pokemon-image-container {
margin-top: 4rem;
border: 1px solid #F2F2F2;
background-color: #F2F2F2;
border-radius: 20px;
padding: 10px;
}
.pokemon-image-detail {
height: 400px;
}
.pokemon-list-details {
margin-top: 20px;
border-width: 0px;
}
.pokemon-box-details {
margin-top: 10px;
}
结束应用
最后,我们需要更新App.js此文件以加载我们创建的组件:
import React from "react";
import Home from "./Home/HomeContainer"; // import the container component
// return the Home component
function App() {
return (
<div className="container">
<Home />
</div>
);
}
export default App;
这样一来,应用程序就应该完成了,但是我们还可以改进其中的许多部分,例如添加属性类型、使用 Redux、重构部分代码、优化打包等等。
您可以在以下代码库PokeApp中获取完整代码。
如果您读到这里,我想对您说声非常感谢,如果您有任何意见或建议,我将不胜感激。
文章来源:https://dev.to/pes/create-pokedex-with-react-hooks-46n1

