1 引入
- 组件之间需要传值
- 父传子 通过绑定
- 子传父 通过方法
- 兄弟之间传值 通过父为中间键
- 指定传值 context
- 组件之间传值需要过滤
- 使用
PropTypes
- 使用
2 传值
2.1 父传子
- children
2.1.1 文件
- 父组件绑定
<Children title={this.state.msg}/>
- 子组件接受
<div>我是子组件====={this.props.title}</div>
convert/children.js
import React ,{Component}from 'react'
export default class Children extends Component {
constructor() {
super();
}
render() {
return (
<div>我是子组件====={this.props.title}</div>
)
}
}
component/index.js
- 优雅暴露所有组件
export {default as Children} from './convert/children'
APP.js
- index.js的入口文件
import React,{Component} from 'react'
import {Children} from './components'
export default class APP extends Component{
constructor() {
super();
this.state={
msg:'我是父组件的值'
}
}
render() {
return(
<div>
<Children title={this.state.msg}/>
</div>
)
}
}
2.1.2 总结
- 使用父组件绑定值,子组件默认props接收
2.1.3 children属性
获取传来的内容
- APP.js
import React,{Component} from 'react'
import {Children} from './components'
export default class APP extends Component{
constructor() {
super();
this.state={
msg:'我是父组件的值'
}
}
render() {
return(
<div>
<Children>
<div>我是DIV</div>
</Children>
</div>
)
}
getSon=(data)=>{
console.log(data)
}
}
- children.js
import React ,{Component}from 'react'
export default class Children extends Component {
constructor() {
super();
this.state={
}
}
render() {
return (
<div>我是子组件 1
<h1>{this.props.children}</h1>
</div>
)
}
}
2.2 子传父
- 思路:
使用方法传值
- 父组件绑定方法(可传参数)
- 子组件接收父组件方法,当调用时
将数据作为参数传给父组件
2.2.1 基础写法
- APP.js
import React,{Component} from 'react'
import {Children} from './components'
export default class APP extends Component{
constructor() {
super();
this.state={
msg:'我是父组件的值'
}
}
render() {
return(
<div>
<Children title={this.state.msg}
myData={this.getSon}/>
</div>
)
}
getSon=(data)=>{
console.log(data)
}
}
- children.js
import React ,{Component}from 'react'
export default class Children extends Component {
constructor() {
super();
this.state={
data:'我是子组件的值'
}
}
render() {
return (
<div>我是子组件====={this.props.title}
<button onClick={this.setData}>传值</button></div>
)
}
setData=()=>{
this.props.myData(this.state.data)
}
}
2.2.2 优雅式写法
- APP.js
import React,{Component} from 'react'
import {Children} from './components'
export default class APP extends Component{
constructor() {
super();
this.state={
msg:'我是父组件的值'
}
}
render() {
return(
<div>
<Children title={this.state.msg}
myData={this.getSon}/>
</div>
)
}
getSon=(data)=>{
console.log(data)
}
}
- children.js
使用了ES6的结构解析(结构赋值)
import React ,{Component}from 'react'
export default class Children extends Component {
constructor() {
super();
this.state={
data:'我是子组件的值'
}
}
render() {
return (
<div>我是子组件====={this.props.title}
<button onClick={this.setData}>传值</button></div>
)
}
setData=()=>{
const { myData} =this.props
myData(this.state.data)
}
}
2.3 兄弟之间传值(上两者结合)
- 核心:
以父组件为中间键
即所有事件和属性在父组件身上- 业务逻辑在父组件上
- 子组件触发
- 思路:
- 子组件1得到父组件的值(父传子)
- 子组件2得到父组件的方法(调用父组件方法,改变值,在父组件中执行)
2.3.1 子组件1得到父组件的值
2.3.2 子组件2得到父组件的值和方法
2.3.3 实现
- APP.js
import React,{Component} from 'react'
import {Children,Children2} from './components'
export default class APP extends Component{
constructor() {
super();
this.state={
step:2,
count:0
}
}
render() {
return(
<div>
<Children title={this.state.count}/>
<Children2
add={this.add}
step={this.state.step}
/>
</div>
)
}
add=()=>{
this.setState(state=>{
return {
count:state.count+=state.step
}
})
}
}
- children.js
import React ,{Component}from 'react'
export default class Children extends Component {
constructor() {
super();
this.state={
}
}
render() {
return (
<div>我是子组件 1
<h1>{this.props.title}</h1>
</div>
)
}
}
- children2.js
import React ,{Component}from 'react'
export default class Children2 extends Component {
constructor() {
super();
this.state={
}
}
render() {
return (
<div>我是子组件 2
<hr/>
<button onClick={this.change}>点我</button>
</div>
)
}
change=()=>{
const {step,add} = this.props
add(step)
}
}
2.3.4 预览
2.4 指定组件传值
- 使用``
2.4.1 基本写法
-
流程:
- 爷爷组件通过传值给父亲组件
state
,父亲props
得到爷爷组件值传给孙子组件,孙子组件调用props
- 爷爷组件通过传值给父亲组件
-
APP.js
import React,{Component} from 'react'
// 爷爷组件
class Grandpa extends Component{
constructor() {
super();
this.state={
msg:'爷爷的红包来了'
}
}
render() {
return(
<div> 爷爷组件<Father msg={this.state.msg} /></div>
)
}
}
// 父亲组件
class Father extends Component{
constructor(props) {
super(props);
}
render() {
return(
<div>父亲组件<Son msg={this.props.msg} />
</div>
)
}
}
// 孙子组件
class Son extends Component{
constructor() {
super();
}
render() {
return(
<div>孙子组件======={this.props.msg}</div>
)
}
}
export default class APP extends Component{
constructor() {
super();
}
render() {
return(
<div><Grandpa/></div>
)
}
}
2.4.2 使用Context写法
const {Provider,Consumer} =React.createContext()
- 不通过父亲组件
- 思路:
- 定义:
const {Provider,Consumer} =React.createContext()
- 源组件:
<Provider value={this.state.msg}> <div>爷爷组件<Father/></div></Provider>
- 目标组件:
<Consumer> { data=>{ return <h1>{data}</h1> } } </Consumer>
一个箭头函数
- 定义:
import React,{Component} from 'react'
const {Provider,Consumer} =React.createContext()
// 爷爷组件
class Grandpa extends Component{
constructor() {
super();
this.state={
msg:'爷爷的红包来了'
}
}
render() {
return(
<Provider value={this.state.msg}> <div>爷爷组件<Father/></div></Provider>
)
}
}
// 父亲组件
class Father extends Component{
constructor(props) {
super(props);
}
render() {
return(
<div>父亲组件<Son/>
</div>
)
}
}
// 孙子组件
class Son extends Component{
constructor() {
super();
}
render() {
return(
<div>
<Consumer>
{
data=>{
return <h1>{data}</h1>
}
}
</Consumer>
</div>
)
}
}
export default class APP extends Component{
constructor() {
super();
}
render() {
return(
<div><Grandpa/></div>
)
}
}
2.4.3 优化封装(组件化)
const {Provider,Consumer} =React.createContext()
- 单独封装的原因:
Provider
爷爷组件使用,Consumer
孙子组件使用,这两个组件都调用时,将创建两次,- 不是同一个
createContext()
对象,孙子组件将得不到值
target文件
- grandpa.js
import React,{Component} from 'react'
import {Provider} from '../createContext'
import Father from './father'
export default class Grandpa extends Component{
constructor() {
super();
this.state={
msg:'爷爷的红包来了'
}
}
render() {
return(
<Provider value={this.state.msg}> <div>爷爷组件<Father/></div></Provider>
)
}
}
- father.js
import React,{Component} from 'react'
import Son from "./son";
export default class Father extends Component{
constructor(props) {
super(props);
}
render() {
return(
<div>父亲组件<Son/>
</div>
)
}
}
- son.js
import React,{Component} from 'react'
import {Consumer} from '../createContext'
export default class Son extends Component{
constructor() {
super();
}
render() {
return(
<div>
<Consumer>
{
data=>{
return <h1>{data}</h1>
}
}
</Consumer>
</div>
)
}
}
createContext()文件
import React from 'react'
const {Provider,Consumer} =React.createContext()
export {
Provider,
Consumer
}
APP.js
import React,{Component} from 'react'
import {Grandpa} from './components'
export default class APP extends Component{
constructor() {
super();
}
render() {
return(
<div><Grandpa/></div>
)
}
}
2.5 过滤
- 使用依赖
prop-types
- 作用:保证其他组件传进来的值是该组件想要的
2.5.1 安装依赖
yarn add prop-types
2.5.2 使用依赖
- 引入依赖
import PropTypes from 'prop-types'
- 使用依赖
Children.propTypes={ title:PropTypes.number }
- children.js
import React ,{Component}from 'react'
import PropTypes from 'prop-types'
export default class Children extends Component {
constructor() {
super();
this.state={
data:'我是子组件的值'
}
}
render() {
return (
<div>我是子组件====={this.props.title}
<button onClick={this.setData}>传值</button></div>
)
}
setData=()=>{
const { myData} =this.props
myData(this.state.data)
}
}
Children.propTypes={
title:PropTypes.number
}
2.5.3 预览
2.5.4 推荐写法
1 无状态组件(外部)
import React ,{Component} from 'react'
import PropTypes from "prop-types";
export default function Rfc (props){
return(
<div>无状态组件
{props.title}
</div>
)
}
Rfc.propTypes={
title:PropTypes.number
}
2 有状态组件(static)
- 在外部也可以使用(违背了类的面向对象编程)
import React ,{Component}from 'react'
import PropTypes from 'prop-types'
export default class Children extends Component {
constructor() {
super();
this.state={
data:'我是子组件的值'
}
}
static propTypes={
title:PropTypes.number
}
render() {
return (
<div>我是子组件====={this.props.title}
<button onClick={this.setData}>传值</button></div>
)
}
setData=()=>{
const { myData} =this.props
myData(this.state.data)
}
}
2.5.5 其他控制
- 控制必填项
- 控制默认值
1 控制必填项
import React ,{Component}from 'react'
import PropTypes from 'prop-types'
export default class Children extends Component {
constructor() {
super();
this.state={
data:'我是子组件的值'
}
}
static propTypes={
title:PropTypes.string.isRequired
}
render() {
return (
<div>我是子组件====={this.props.title}
<button onClick={this.setData}>传值</button></div>
)
}
setData=()=>{
const { myData} =this.props
myData(this.state.data)
}
}
2 控制默认值
- 控制默认值
有状态组件
static defaultProps={
title:'崽崽'
}
无状态组件
Rfc.propTypes={
title:PropTypes.string
}
Rfc.defaultProps={
title:"再砸"
}
3 拓展
3.1 购物车实现
3.1.1 基本实现
- 思路:
- 1 使用
map
将数据遍历,渲染到页面 - 2 将数据结构搭建好
- 3 为
-
+
两个按钮帮上id
- 4 为按钮绑定点击事件
- 点击时
map
循环,判断是否与目标的id相等e.target.dataset.id
- 如果相等,将
el.id
执行操作,并且修改总价
- 点击时
- 5 购物车总价实现
- 定义一个临时变量
temp
,遍历forEach
算出每一个总价 加到temp
- 在每次
-
+
时调用,async + await - 在生命周期中完成首次渲染
componentDidMount
- 定义一个临时变量
- 1 使用
import React,{Component} from 'react'
export default class Cart extends Component{
constructor() {
super();
this.state={
allPrice:0,
carts:[{
id: 1,
name: '足球鞋',
price: 25,
count: 1,
total: 25
}, {
id: 2,
name: '篮球鞋',
price: 35,
count: 1,
total: 35
}, {
id: 3,
name: '草鞋',
price: 125,
count: 1,
total: 125
}]
}
this.reduce=this.reduce.bind(this)
this.add=this.add.bind(this)
}
componentDidMount() {
this.getAll()
}
render() {
return(
<div>
<ul>
{
this.state.carts.map(el=><li key={el.id}>
<h1 >{el.name}</h1>
<h3><span>价格:${el.price}</span>
<button onClick={this.reduce} data-id={el.id}>-</button>
<input value={el.count} readOnly/>
<button onClick={this.add} data-id={el.id}>+</button>
<span>总价:{el.total}</span>
</h3>
</li>)}
<h1>总价:{this.state.allPrice}</h1>
</ul>
</div>
)
}
async reduce(e){
await this.setState({
carts: this.state.carts.map(el=>{
if(el.id===Number(e.target.dataset.id)) {
el.count--
el.total=el.count*el.price
}
return el
})
})
this.getAll()
}
async add(e){
await this.setState({
carts: this.state.carts.map(el=>{
if(el.id===Number(e.target.dataset.id)) {
el.count++
el.total=el.count*el.price
}
return el
})
})
this.getAll()
}
getAll(){
let temp = 0
this.state.carts.forEach(el=>{
temp +=el.count*el.price
})
this.setState({
allPrice:temp
})
}
}
3.1.2 组件化实现
- 分析:
- 1 每一项数据都是一个子组件
- 2 子组件可以对自己的值进行修改(可控组件)
- 3 涉及组件之间传值
- 父组件给子组件传数据(遍历)
- 子组件接收方法,控制父组件的总价
- 思路:
- 1 创建每一项为子组件
- 2 创建父组件
- 定义数据,方法,
- 定义数据,方法,
- carts.js
import React,{Component} from 'react'
import CartItem from "./cartItem";
export default class Carts extends Component{
constructor() {
super();
this.state={
allPrice:0,
carts:[{
id: 1,
name: '足球鞋',
price: 25,
count: 1,
total: 25
}, {
id: 2,
name: '篮球鞋',
price: 35,
count: 1,
total: 35
}, {
id: 3,
name: '草鞋',
price: 125,
count: 1,
total: 125
}]
}
}
render() {
return(
<div>
{this.state.carts.map(el=> <CartItem {...el}
add={this.add}
reduce={this.reduce}
all={this.getAll}
key={el.id}/>)}
<h1>总价:{this.state.allPrice}</h1>
</div>
)
}
add=(id)=>{
this.setState({
carts: this.state.carts.map(el=>{
if(id===el.id){
el.count++
}
return el
})
})
}
reduce=(id)=>{
this.setState({
carts: this.state.carts.map(el=>{
if(id===el.id){
el.count--
}
return el
})
})
}
getAll=()=>{
let temp =0
this.state.carts.map(el=>{
temp+=el.price*el.count
})
this.setState({
allPrice:temp
})
}
}
- cartItem.js
import React,{Component} from 'react'
export default class CartItem extends Component{
constructor() {
super();
this.reduce=this.reduce.bind(this)
this.add=this.add.bind(this)
}
componentDidMount() {
this.props.all()
}
render() {
return(
<div>
<ul>
{
<li>
<h1>{this.props.name}</h1>
<h3><span>价格:${this.props.price}</span>
<button onClick={this.reduce} >-</button>
<input value={this.props.count} readOnly />
<button onClick={this.add} >+</button>
<span>总价:{this.props.price*this.props.count}</span>
</h3>
</li>}
</ul>
</div>
)
}
async reduce(){
await this.props.reduce(this.props.id)
this.props.all()
}
async add(){
await this.props.add(this.props.id)
this.props.all()
}
}
3.2 温度转换实现
- 分析:
- 父组件绑定值,定义要改变的方法,子组件触发
- 子组件触发时,传入ID
- 两个子组件
child1
child2
,一个父组件APP
children1.js
import React ,{Component}from 'react'
export default class Children1 extends Component{
constructor() {
super();
}
render() {
return(
<div>
<fieldset>
<legend>输入温度,摄氏温度:</legend>
<input type="text" onChange={this.change} value={this.props.s}/>
</fieldset>
</div>
)
}
change=(e)=>{
this.props.sChange(e.target.value)
}
}
children2.js
import React,{Component} from 'react'
export default class Children2 extends Component{
constructor() {
super();
}
render() {
return(
<div>
<fieldset>
<legend>输入温度,华氏温度:</legend>
<input type="text" value={this.props.h} onChange={this.change} />
</fieldset>
</div>
)
}
change=(e)=>{
this.props.hChange(e.target.value)
}
}
APP.js
import React,{Component} from 'react'
import {Child1,Child2} from './components/index'
export default class APP extends Component{
constructor() {
super();
this.state={
s:0,
h:32,
flag:true
}
this.sChange=this.sChange.bind(this)
}
render() {
return(
<div>
<Child1 s={this.state.s} sChange={this.sChange} />
<Child2 h={this.state.h} hChange={this.hChange}/>
<h1>{this.state.flag?'水还没有沸腾':'水沸腾了'}</h1>
</div>
)
}
async sChange(value){
await this.setState(state=>{
return {
s:value ,
h:32+Number(value)*1.8,
}
})
if( Number(this.state.h)>100){
this.setState({
flag:false
})
}else {
this.setState({
flag:true
})
}
}
hChange=(value)=>{
this.setState({
h:value,
s:(Number(value)-32)/1.8
})
console.log(this.state.h)
}
}