本章内容
目录
- 父组件向子组件传递数据
- 子组件向父组件传递数据
上一节内容我们补充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
- 运行界面,发现删除功能完美实现
到此,本章内容结束!