转载请注明出处,未经同意,不可修改文章内容。
🔥🔥🔥"前端一万小时"两大明星专栏------"从零基础到轻松就业"、"前端面试刷题",已于本月大改版,合二为一,干货满满,欢迎点击公众号菜单栏各模块了解。
javascript
涉及面试题:
1. 什么是无状态组件?
2. 什么是有状态组件?
3. React Redux 中展示组件和容器组件之间的区别是什么?
4. 组件和不同类型?
编号:[react_25]
1 "UI 组件"和"容器组件"的拆分
紧接上篇代码,请自行打开 TodoList.js
文件。
这个组件里边包含了很多内容,但在实际代码编写过程中,这种把"如何渲染 "和"逻辑执行"放在一起的编码方式,会给后期代码维护造成很大的不便。
故,我们需要对这个组件进行拆分:
- 让"UI 组件"专门做组件的"渲染";
- 让"容器组件"去处理组件的逻辑。
1️⃣在 src
目录下创建一个"UI 组件"------ TodoListUI.js
:
2️⃣参照之前所学,编写"UI 组件"的相应代码(先将 TodoList.js
文件里关于"页面渲染"的代码,全部剪切并粘贴至"UI 组件"):
先打开 TodoList.js
文件,剪切里边"页面渲染"相关的代码:
jsx
import React, {Component} from "react";
import 'antd/dist/antd.css';
import { Input, Button, List } from 'antd';
import store from "./store";
import {getInputChangeAction, getAddItemAction, getDeleteItemAction} from "./store/actionCreators";
import TodoListUI from "./TodoListUI"; /*
2️⃣-②:从当前目录下的 TodoList.js 文件中,
引入 TodoListUI 组件;
*/
class TodoList extends Component {
constructor(props) {
super(props);
this.state = store.getState();
this.handleInputChange = this.handleInputChange.bind(this);
this.handleStoreChange = this.handleStoreChange.bind(this);
this.handleButtonClick = this.handleButtonClick.bind(this);
store.subscribe(this.handleStoreChange);
}
render() {
return <TodoListUI /> /*
2️⃣-③:直接返回"UI 组件"。
即,页面具体要显示什么,由"UI 组件"决定;
*/
/*
2️⃣-①:先将这部分与"渲染页面"有关的代码剪切;
(
<div style={{marginTop: "10px", marginLeft: "10px"}}>
<div>
<Input
value={this.state.inputValue}
placeholder="todo info"
style={{width: "300px", marginRight: "10px"}}
onChange={this.handleInputChange}
/>
<Button type="primary" onClick={this.handleButtonClick}>提交</Button>
<List style={{marginTop: "10px", width: "300px"}}
bordered
dataSource={this.state.list}
renderItem={(item, index) => <List.Item onClick = {this.handleItemDelete.bind(this, index)}>{item}</List.Item>}
/>
</div>
</div>
)
*/
}
handleInputChange(e) {
const action = getInputChangeAction(e.target.value)
store.dispatch(action);
}
handleStoreChange() {
this.setState(store.getState());
}
handleButtonClick() {
const action = getAddItemAction();
store.dispatch(action);
}
handleItemDelete(index) {
const action = getDeleteItemAction(index);
store.dispatch(action);
}
}
export default TodoList;
打开 TodoListUI.js
文件,将 2️⃣-① 中剪切的部分,粘贴到这个"UI 组件"里:
jsx
import React, {Component} from "react";
class TodoListUI extends Component {
render() { // 2️⃣-④:将 TodoList 里关于"页面渲染"的代码,全部剪切并粘贴至此;
return(
<div style={{marginTop: "10px", marginLeft: "10px"}}>
<div>
<Input
value={this.state.inputValue}
placeholder="todo info"
style={{width: "300px", marginRight: "10px"}}
onChange={this.handleInputChange}
/>
<Button type="primary" onClick={this.handleButtonClick}>提交</Button>
<List style={{marginTop: "10px", width: "300px"}}
bordered
dataSource={this.state.list}
renderItem={(item, index) => <List.Item onClick = {this.handleItemDelete.bind(this, index)}>{item}</List.Item>}
/>
</div>
</div>
)
}
}
export default TodoListUI;
看下页面效果( TodoList.js
文件里有很多报错):
3️⃣打开 TodoListUI.js
文件,我们来挨着排除错误(请跟着我的编号,阅读以下 TodoList.js
和 TodoList.js
文件):
🔗前置知识:《React 入门------⑤ 拆分组件和组件间传值》
TodoListUI.js
文件:
jsx
import React, {Component} from "react";
// 3️⃣-③:并粘贴至"UI 组件"里;
import { Input, Button, List } from 'antd';
class TodoListUI extends Component {
render() {
return(
<div style={{marginTop: "10px", marginLeft: "10px"}}>
<div>
{/*
3️⃣-①:Input 标签是从 Antd Design 引入的,
故需要在这个组件中引入,而不是在 TodoList 组件中引入;
*/}
{/*
3️⃣-④:对于 value 值,在"UI 组件"中,它是没有 state 的,
state 是 TodoList 组件里的。故,需要"组件间传值"的方式,取得这个 state;
value={this.state.inputValue}
*/}
{/* 3️⃣-⑥:"UI 组件"接收 TodoList 组件传过来的值; */}
{/*
3️⃣-⑦:同理,handleInputChange 函数是在 TodoList 组件里定义的,
也需要通过"属性"的形式传递过来;
onChange={this.handleInputChange}
*/}
{/* 3️⃣-⑨:"UI 组件"接收 TodoList 组件传过来的值; */}
<Input
value={this.props.inputValue}
placeholder="todo info"
style={{width: "300px", marginRight: "10px"}}
onChange={this.props.handleInputChange}
/>
{/*
3️⃣-⑩:同理,handleButtonClick 也需要从 TodoList 中传过来;
<Button type="primary" onClick={this.handleButtonClick}>提交</Button>
*/}
{/* 3️⃣-⑫:"UI 组件"接收 TodoList 组件传过来的值; */}
<Button type="primary" onClick={this.props.handleButtonClick}>提交</Button>
{/*
3️⃣-⑬:同理,state 也需要从 TodoList 中传过来;
dataSource={this.state.list}
*/}
{/* 3️⃣-⑮:"UI 组件"接收 TodoList 组件传过来的值; */}
{/*
3️⃣-⑯:同理,handleItemDelete 也需要从 TodoList 中传过来;
renderItem={(item, index) => <List.Item onClick = {this.handleItemDelete.bind(this, index)}>{item}</List.Item>}
*/}
{/*
3️⃣-⑲:"UI 组件"接收 TodoList 组件传过来的值(
❗️请注意我 index 是怎样用"箭头函数"传进去的);
*/}
<List
style={{marginTop: "10px", width: "300px"}}
bordered
dataSource={this.props.list}
renderItem={(item, index) => (<List.Item onClick = {() => {this.props.handleItemDelete(index)}}>{item}</List.Item>)}
/>
</div>
</div>
)
}
}
export default TodoListUI;
TodoList.js
文件:
jsx
import React, {Component} from "react";
import 'antd/dist/antd.css';
/*
3️⃣-②:把 Antd Design 关于"页面渲染"的内容剪切;
import { Input, Button, List } from 'antd';
*/
import store from "./store";
import {getInputChangeAction, getAddItemAction, getDeleteItemAction} from "./store/actionCreators";
import TodoListUI from "./TodoListUI";
class TodoList extends Component {
constructor(props) {
super(props);
this.state = store.getState();
this.handleInputChange = this.handleInputChange.bind(this);
this.handleStoreChange = this.handleStoreChange.bind(this);
this.handleButtonClick = this.handleButtonClick.bind(this);
// 3️⃣-⑱:在这里对 handleItemDelete 方法的 this 指向作一个修改;
this.handleItemDelete = this.handleItemDelete.bind(this);
store.subscribe(this.handleStoreChange);
}
render() {
return(
/*
3️⃣-⑤、3️⃣-⑧、3️⃣-⑪、3️⃣-⑭、3️⃣-⑰:
通过"属性"的形式传值给 TodoListUI 组件;
*/
<TodoListUI
inputValue={this.state.inputValue}
list={this.state.list}
handleInputChange={this.handleInputChange}
handleButtonClick={this.handleButtonClick}
handleItemDelete={this.handleItemDelete}
/>
)
}
handleInputChange(e) {
const action = getInputChangeAction(e.target.value)
store.dispatch(action);
}
handleStoreChange() {
this.setState(store.getState());
}
handleButtonClick() {
const action = getAddItemAction();
store.dispatch(action);
}
handleItemDelete(index) {
const action = getDeleteItemAction(index);
store.dispatch(action);
}
}
export default TodoList;
看看页面效果(正常运行,无报错):
2 无状态组件
紧接上边的代码,打开 TodoListUI.js
文件,这里边我们把 TodoListUI 定义成了一个"类"。
但,实际编码中,当我们去定义一个"UI 组件"的时候,如果这个组件只负责"页面渲染"(即,只有一个 render 函数),且没有其他任何逻辑操作时,我们一般直接用"无状态组件"来定义"UI 组件"。
"无状态组件"可以简单理解为一个"函数"。
之所以要用"函数"来改写,是为了"提高性能":
- 通过 class 生成的"类"里边会有很多"生命周期函数 "要执行,
render
函数只是其中之一; - 通过"函数"(无状态组件)改写后,只需要执行一个函数即可。
jsx
/*
2️⃣-④:由于这里没用到 Component,故删掉!
import React, {Component} from "react";
*/
import React from "react";
import { Input, Button, List } from 'antd';
// 2️⃣然后改写成一个函数;
const TodoListUI = (props) => { // 2️⃣-①:这个函数会接收一个"参数"props;
return(
// 2️⃣-②:然后返回一段 JSX;
// 2️⃣-③:❗️注意用 props 替换 this.props。
<div style={{marginTop: "10px", marginLeft: "10px"}}>
<div>
<Input
value={props.inputValue}
placeholder="todo info"
style={{width: "300px", marginRight: "10px"}}
onChange={props.handleInputChange}
/>
<Button type="primary" onClick={props.handleButtonClick}>提交</Button>
<List
style={{marginTop: "10px", width: "300px"}}
bordered
dataSource={props.list}
renderItem={(item, index) => (<List.Item onClick = {() => {props.handleItemDelete(index)}}>{item}</List.Item>)}
/>
</div>
</div>
)
}
/*
1️⃣直接将以下代码全部删除;
class TodoListUI extends Component {
render() {
return(
<div style={{marginTop: "10px", marginLeft: "10px"}}>
<div>
<Input
value={this.props.inputValue}
placeholder="todo info"
style={{width: "300px", marginRight: "10px"}}
onChange={this.props.handleInputChange}
/>
<Button type="primary" onClick={this.props.handleButtonClick}>提交</Button>
<List
style={{marginTop: "10px", width: "300px"}}
bordered
dataSource={this.props.list}
renderItem={(item, index) => (<List.Item onClick = {() => {this.props.handleItemDelete(index)}}>{item}</List.Item>)}
/>
</div>
</div>
)
}
}
*/
export default TodoListUI;
看看页面效果(正常运行,无报错):
祝好,qdywxs ♥ you!