本章内容
上一节内容我们补充l了在 React
使用 JSX
语法的一些细节。本节我们继续使用 ”TodoList“ 案例来讲解一下”组件拆分与组件传值“
父组件向子组件传递数据
-
打开一开始我们已经创建好的工程,现在我们用”组件化“的思想去改写
TodoList
组件 -
那我们应该怎么去拆分组件呢?打开入口文件
index.js
时,我们可以清楚的看到TodoList
就是被当成一个大组件挂载到id=”root“
的元素上。
-
界面运行后的显示内容也是
TodoList
组件的所有内容,所以最外层的”大组件“我们知道是谁了,那么我们就可以对这个”大组件“进行拆分.
-
组件拆分好后,我们在
src
目录下新增一个TodoItem.js
, 按照之前的学习知识,我们在TodoItem
这个组件里写一些初始化的代码
// TodoItem.js 组件
import React, { Component } from 'react'
class TodoItem extends Component {
render () {
return (
<div>
我是组件 TodoItem
</div>
)
}
}
export default TodoItem
- 现在我们试着在
TodoList.js
组件中引入TodoItem.js
组件
import React, { Component, Fragment } from "react";
import TodoItem from "./TodoItem"; // 1、引入 TodoItem 组件
class TodoList extends Component{
constructor(props) {
super(props) // ES6 的语法
this.state = {
inputValue: '',
list: []
}
}
render() {
return (
<Fragment>
<div>
<input value={this.state.inputValue} onChange={this.changeInputValue.bind(this)} />
<button onClick={this.addListData.bind(this)}> 提交 </button>
</div>
<ul>
{this.state.list.map((item, index) => {
// 2、将之前循环 list 数据项时创建的 li 标签注释掉,改为使用 ”组件“的形式来编写代码,这部分代码统一封装在 TodoItem 里
// return (<li key={index} onClick={(this.deleteData.bind(this,index))}> {item} </li>)
// 3、不渲染 li 标签了,取而代之的是,使用 TodoItem 组件来渲染内容
return ( <TodoItem></TodoItem>)
})}
</ul>
</Fragment>
)
}
deleteData(index) {
const list = [...this.state.list]
list.splice(index, 1)
this.setState({
list: list
})
}
addListData() {
this.setState({
list: [...this.state.list, this.state.inputValue]
})
this.setState({
inputValue: ''
})
}
changeInputValue(e) {
this.setState({
inputValue: e.target.value
})
}
}
export default TodoList
-
运行一下代码,我们会发现有了效果,也没有报错(有一个让添加
key
的警告,我们后续再解决)。但我们发现列表显示的内容并不是我们输入框输入的内容,而是组件TodoItem
里的内容
-
那怎么样才能将输入框的数据传到组件
TodoItem
里并正确显示呢?这就要使用到”组件间传值“的知识点嘞 -
如图所示,实际开发过程中,我们会将复杂的界面拆分成一个”组件树“。组件间存在着各种关系(父子、祖先、相邻等)。而在我们本章案例里的
TodoList
和TodoItem
属于父子关系
-
在
React
中,父组件可以通过”属性“的形式向子组件进行传递数据。所以如果TodoList
要将input
输入框的数据传递给 子组件TodoItem
,可以直接定义一个TodoItem
的任意属性,然后通过这个属性进行传值
import React, { Component, Fragment } from "react";
import TodoItem from "./TodoItem";
class TodoList extends Component{
constructor(props) {
super(props) // ES6 的语法
this.state = {
inputValue: '',
list: []
}
}
render() {
return (
<Fragment>
<div>
<input value={this.state.inputValue} onChange={this.changeInputValue.bind(this)} />
<button onClick={this.addListData.bind(this)}> 提交 </button>
</div>
<ul>
{this.state.list.map((item, index) => {
// 在 map 方法中,回调函数的第一个“形参”即为列表(数组)的“每一项 item”,我们又规定了回调函数返回的内容就是这个“每一项 item”。
// 所以,我们把“每一项 item”起名为 content,并以“属性”的形式传给“子组件 TodoItem”
return ( <TodoItem content={item}></TodoItem>)
})}
</ul>
</Fragment>
)
}
deleteData(index) {
const list = [...this.state.list]
list.splice(index, 1)
this.setState({
list: list
})
}
addListData() {
this.setState({
list: [...this.state.list, this.state.inputValue]
})
this.setState({
inputValue: ''
})
}
changeInputValue(e) {
this.setState({
inputValue: e.target.value
})
}
}
export default TodoList
- 相应的,在
React
中,子组件可以通过this.props.属性
的形式来接收父组件传递的数据
import React, { Component } from 'react'
class TodoItem extends Component {
render () {
return (
<div>
{/* 子组件可以通过 this.props.属性 的形式来接收父组件传递的数据 */}
{this.props.content}
</div>
)
}
}
export default TodoItem
- 接着我们再次运行界面,发现输入框输入啥内容提交后,界面就相应的显示其内容数据
子组件向父组件传递数据
-
紧着着上面的代码,我们要接着实现”当点击列表中的某项内容时,该内容从列表中删除“
-
首先按照需求,我们应该在列表项上绑定一个点击事件。如今的列表项已经被拆成了”小组件“
TodoItem
, 所以我们要打开TodoItem.js
文件,去给相应的元素绑定点击事件
import React, { Component } from 'react'
class TodoItem extends Component {
render () {
return (
// 1、绑定点击事件
<div onClick={this.handleClick.bind(this)}>
{this.props.content}
</div>
)
}
// 2、点击事件的逻辑放在 handleClick 方法中
handleClick() {
}
}
export default TodoItem
- ”点击事件“绑定后,我们继续分析以下问题
1、需要确定点击的是列表的哪一项---------可以通过 map 的 index 来确定点击哪一项,并传给 TodoItem 组件
2、通过 index 知道点击的是哪一项后,就要考虑怎么把该项从列表中删除-------子组件TodoItem 的某项被点击时,实质上是将父组件TodoList的”list 数据某项删除“。在父组件中,已经定义了一个删除的方法 ”deleteData“,那么我们可以把这个方法通过属性传值方式传给子组件 TodoItem ,然后子组件自己触发”点击事件“的方法中就可以直接调用父组件传递过来的”删除方法“
- 按照这个思路,我们来编写代码
// TodoList.js 组件
import React, { Component, Fragment } from "react";
import TodoItem from "./TodoItem";
class TodoList extends Component{
constructor(props) {
super(props) // ES6 的语法
this.state = {
inputValue: '',
list: []
}
}
render() {
return (
<Fragment>
<div>
<input value={this.state.inputValue} onChange={this.changeInputValue.bind(this)} />
<button onClick={this.addListData.bind(this)}> 提交 </button>
</div>
<ul>
{this.state.list.map((item, index) => {
// 1、传递 item 给子组件用于内容渲染
// 2、传递 index 给子组件,让子组件触发自身的删除事件时,知道是哪一项被删除
// 3、传递 deleteData 方法给子组件,让子组件触发自身的删除事件时进行调用
return ( <TodoItem content={item} index={index} deleteFn={this.deleteData.bind(this)}></TodoItem>)
})}
</ul>
</Fragment>
)
}
deleteData(index) {
const list = [...this.state.list]
list.splice(index, 1)
this.setState({
list: list
})
}
addListData() {
this.setState({
list: [...this.state.list, this.state.inputValue]
})
this.setState({
inputValue: ''
})
}
changeInputValue(e) {
this.setState({
inputValue: e.target.value
})
}
}
export default TodoList
// TodoItem.js 组件
import React, { Component } from 'react'
class TodoItem extends Component {
render () {
return (
<div onClick={this.handleClick.bind(this)}>
{this.props.content}
</div>
)
}
handleClick() {
// 4、调用从父组件中传递过来的删除方法,传入删除项的 index
this.props.deleteFn(this.props.index)
}
}
export default TodoItem
- 运行界面,发现删除功能完美实现
到此,本章内容结束!