使用 React 和 Redux 进行可重用组件
介绍
可重用组件是 React 的核心。组件有两种形式:
- 展示性或“哑”组件
- 容器组件
展示组件通常与根据输入呈现 HTML UI 有关。输入可以是静态内容,也可以是从外部 API 检索到的动态内容。除了展示目的所需的内部状态外,它们不管理任何状态。按钮、列表、输入就是其中的一些示例。
另一方面,容器组件更加强大。它们除了 UI 功能外还包含一些逻辑。使用 redux 管理应用程序状态时,我们经常发现很难决定赋予组件的控制级别。在结构完美的应用程序中,展示组件和容器组件之间应该保持良好的平衡。
首先,让我们快速看一下今天要构建的内容。我们将使用以下 npm 包,
"react": "^16.8.6",
"react-dom": "^16.8.6",
"react-redux": "^7.0.3",
"react-router-dom": "^5.0.0",
"redux": "^4.0.1"
“Noogle”——搜索引擎
我们的任务是制作一个简单的搜索引擎的前端,我们将其称为“Noogle”。它基本上有两个页面:
- 主页:我们只有一个长搜索栏
- 结果页面:我们在此显示搜索结果以及搜索栏
作为开发者,我们第一步要做的就是对应用进行组件分解。一眼望去,我们知道有两个页面,也就是两个组件。我们称之为页面组件。
App
|- HomePageComponent
|- ResultsPageComponent
在 HomePageComponent 中,我们有一个搜索组件,它是一个带有按钮的文本框。ResultsPageComponent 有一个搜索组件和一个结果列表组件。结果列表组件是搜索结果的列表。所以,现在我们的细分如下:
App
|- HomePageComponent
|- HomeSearchComponent
|- ResultsPageComponent
|- ResultsSearchComponent
|- ResultsListComponent
现在我们可以观察到 HomeSearchComponent 和 ResultsSearchComponent 实际上有类似的用法。两者都接受文本输入并在单击按钮时触发一些操作。这就是可重用性发挥作用的地方。我们不是使用两个单独的组件,而是在两个实例中重复使用单个组件。
App
|- HomePageComponent
|- SearchComponent
|- ResultsPageComponent
|- SearchComponent
|- ResultsListComponent
创建应用程序
让我们首先使用React CLI创建一个新的 React 应用程序。在你的终端中,使用以下命令生成该应用程序:
$ create-react-app noogle
$ cd noogle
接下来我们将 redux 和 react-router 添加到我们的项目中:
$ npm install --save redux react-redux react-router-dom
完成后,我们将创建两个页面组件以及一个用于导航页面的简单路由器。我们还初始化 redux 的 store、actions 和 Reducer。SRCsrc 的目录结构如下。
src
|- components
|- SearchComponent.js
|- pages
|- HomePageComponent.js
|- ResultsPageComponent.js
actions.js
App.css
App.js
index.css
index.js
reducers.js
store.js
为了确保 Noogle 看起来很棒,我编写了一些 CSS 添加项。您可以使用这些添加项,也可以自己发挥创意。另外,不要忘记将 bootstrap 链接到index.html。
/** index.css **/
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
monospace;
}
.homepage{
width: 800px;
margin-left: auto;
margin-right: auto;
margin-top: 200px;
text-align: center;
}
.homepage h1{
margin-bottom: 30px;
}
.homepage input{
margin-bottom: 15px;
}
.resultspage{
margin-top: 50px;
}
.resultspage .search{
margin-bottom: 50px;
margin-top: 20px;
}
.resultspage input{
display: inline-block;
width: 500px;
margin-right: 15px;
}
.resultspage .result{
width: 800px;
border: 1px solid #ccc;
padding: 10px 10px 10px 10px;
margin-bottom: 10px;
}
.resultspage .result .title{
color: #1a0dab;
font-size: 20px;
}
.resultspage .result .url{
color: #006621;
}
首先,让我们为我们的应用创建一个 Router,以方便页面转换。与此同时,我们将初始化 redux 样板。
// HomePageComponent.js
import React, { Component } from 'react';
class HomePageComponent extends Component{
render(){
return (
<div className="container">
<h1>Home Page</h1>
</div>
)
}
}
export default HomePageComponent;
// ResultsPageComponent.js
import React, { Component } from 'react';
class ResultsPageComponent extends Component{
render(){
return (
<div className="container">
<h1>Results Page</h1>
</div>
)
}
}
export default ResultsPageComponent;
// reducers.js
const initialState = {
}
function searchReducer(state, action) {
if (typeof state === 'undefined') {
return initialState
}
// We create an empty reducer for now
return state
}
// store.js
import { createStore } from 'redux'
import searchReducer from './reducers'
const store = createStore(todoApp)
// App.js
import React from 'react';
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import HomePageComponent from './pages/HomePageComponent';
import ResultsPageComponent from './pages/ResultsPageComponent';
import { Provider } from 'react-redux'
import store from './store';
function App() {
return (
<Provider store={store}>
<Router>
<div>
<Route path="/" exact component={HomePageComponent} />
<Route path="/results" component={ResultsPageComponent} />
</div>
</Router>
</Provider>
);
}
export default App;
现在,使用$ npm start运行应用程序并测试它在浏览器中的外观。当您分别导航到https://localhost:3000和https://localhost:3000/results时,它应该显示主页和结果页面。
构建 SearchComponent
SearchComponent 由一个文本输入框和一个按钮组成。按钮的目的是使用文本输入框中的查询来执行搜索过程。首先,我们在components目录中创建 SearchComponent。我们可以使用 components 目录来存储应用中的共享组件。
// SearchComponent.js
import React, { Component } from 'react';
class SearchComponent extends Component{
state = {
query: ""
}
onInputChange = (e) => {
this.setState({
query: e.target.value
})
}
onButtonClick = (e) => {
//Todo: fire the search processes
}
render(){
return (
<div className="search">
<input type="text"
value={this.state.query}
onChange={this.onInputChange}
/>
<button onClick={this.onButtonClick}>Search</button>
</div>
)
}
}
export default SearchComponent;
在此示例中,我们使用状态管理模式,其中 UI 状态在内部进行管理 - 即:输入字段的状态在组件内部进行管理。虽然我们有 redux 来进行状态管理,但用与整体应用状态无关的状态来塞满 redux 存储是一个错误。另一种模式是使用专用的 redux 存储来处理 UI 状态。
完成组件 UI 后,让我们看看如何使实际的搜索工作。
让搜索发挥作用
为了使本指南简洁明了,我们不会将任何实际 API 连接到应用程序。相反,我们将在商店中对一些值进行硬编码,并使用简单的文本过滤来模拟搜索行为。简而言之,我们将有一个固定的搜索项数组。一旦触发查询,我们将过滤搜索项以匹配查询。然后我们在结果视图中显示这些项目。以下代码修改了 Reducer 和操作以添加此行为:
// actions.js
// These are our action types
export const SEARCH_LIST = "SEARCH_LIST"
// Now we define actions
export function searchList(query){
return {
type: SEARCH_LIST,
query
}
}
// reducers.js
import { SEARCH_LIST } from './actions';
const initialState = {
results: []
}
// Mock data
const data = [
{
"title": "React – A JavaScript library for building user interfaces",
"url": "https://reactjs.org/"
},
{
"title": "Tutorial: Intro to React – React",
"url": "https://reactjs.org/tutorial/tutorial.html"
},
{
"title": "GitHub - facebook/react: A declarative, efficient, and flexible JavaScript",
"url": "https://github.com/facebook/react"
},
{
"title": "What is React - W3Schools",
"url": "https://www.w3schools.com/whatis/whatis_react.asp"
},
]
export default function searchReducer(state, action) {
if (typeof state === 'undefined') {
return initialState
}
if(action.type === SEARCH_LIST){
const results = [];
for(var item of data){
if(item.title.indexOf(action.query) !== -1){
results.push
免责声明:本内容来源于第三方作者授权、网友推荐或互联网整理,旨在为广大用户提供学习与参考之用。所有文本和图片版权归原创网站或作者本人所有,其观点并不代表本站立场。如有任何版权侵犯或转载不当之情况,请与我们取得联系,我们将尽快进行相关处理与修改。感谢您的理解与支持!
请先 登录后发表评论 ~