【React】react 父组件怎么获取子组件的这个值?
react 父组件怎么获取子组件的这个price
值?
父组件:
class Flight extends Component{render() {
return (
<div>
<Item />
</div>
)
}
}
子组件:
export default class Item extends Component {constructor(props) {
super(props);
this.state = {
names: ["(1)免费行李","2","3"],
values: ["1","2","3"],
selectName: '',
prices: '0'
}
}
handleChange(e){
var value = e.target.value;
var price = 800;
if(value == "1"){
price = 0
}else{
price = (value - 1) * price
}
this.setState({
selectName: value,
prices: price
})
//这个值怎么传给父组件
console.log(price)
}
render() {
var options = [];
var prices = this.state.prices;
for (var option in this.state.names) {
options.push(
<option key={this.state.values[option]} value={this.state.values[option]}> {this.state.names[option]}</option>
)
};
return (
<div className={styles.item}>
<select className={styles.select} onChange={this.handleChange.bind(this)}>
{options}
</select>
<p>{prices}</p>
</div>
)
}
}
求解。
回答
概念上,组件是封闭的资料环境。React中是单向资料流的设计,也就是是说只有父组件传递资料给子组件这回事。以正确的技术说明,是 拥有者组件 可以设置 被拥有者组件 中的资料,也就是主人与仆人的关系。
那么子组件要如何与父组件沟通这件事,简单的来说,是一种迂回的作法,在父组件中设置了一个方法(函数),然后传递给子组件的props,子组件在事件触发时,直接呼叫这个props所设置的方法(函数)。但这中间,有谁(对象)呼叫了函数的设置,也就是this
要对得住,对不到正确的对象,就不会正常作用。
父组件到子组件用props设置,子组件到父组件用上面说的方式,这是基本的套路,但它只适用于简单的组件结构,因为它相当麻烦,而且不灵活。那么如果要作到子组件与子组件要彼此沟通这件事,就不是太容易。当然,我想你已经听过,复杂的应用需要额外使用flux或redux来解决这问题,这是必经之路。
不过,在思考整体的React应用设计时,要有应用领域状态,也就是全局状态的概念。第一是应用领域state(状态)的,通常会在父组件中,而不是子组件中,子组件有可能很多,位于树状结构很深的地方。当然你这应用还简单,但思维总是要慢慢建立。
上面原理说完了,看代码应该会更佳理解,下面你的例子再简化一下的例子:
这是你的Item组件:
import React, { Component } from 'react'export default class Item extends Component {
constructor(props) {
super(props)
this.state = {
names: ['(1)免费行李','2','3'],
values: ['1','2','3'],
selectName: '',
prices: '0'
}
}
handleChange(e){
var value = e.target.value;
var price = 800;
if(value == '1'){
price = 0
}else{
price = (value - 1) * price
}
this.setState({
selectName: value,
prices: price
})
//这个值怎么传给父组件
//用传过来的changePrice属性(props),是个函数,呼叫它把price交给父组件中的函数去处理
this.props.changePrice(price)
}
render() {
var options = [];
var prices = this.state.prices;
for (var option in this.state.names) {
options.push(
<option key={this.state.values[option]} value={this.state.values[option]}> {this.state.names[option]}</option>
)
}
return (
<div>
<select onChange={this.handleChange.bind(this)}>
{options}
</select>
<p>{prices}</p>
</div>
)
}
}
这是父组件,我用App.js代表:
import React, { Component } from 'react';import Item from './Item'
class App extends Component {
constructor(props) {
super(props)
this.state = {price: 0}
}
//给子组件用来传price用的方法
changePrice(price){
this.setState({price: price})
}
render() {
return (
<div>
<Item changePrice={this.changePrice.bind(this)}/>
<p>{this.state.price}</p>
</div>
);
}
}
export default App;
上面解决了你的问题。但下面是我想多写的,因为代码中有很多撰写风格的问题。要一步就到位当然不可能,可以渐进式来作。
1. 先绑定(bind)住render有用到的方法
在父组件与子组件各有用到一个自己的方法changePrice,并在render中作赋值,在React中需要bind过才会把this对住,因为在render的return语句中使用,它在重渲染(re-render)时会再次建立新的方法(函数)内容值,这样会有效能上的影响,所以要先作绑定的事,然后再render的return里面用。
先绑定要在类的contructor
里作,像下面这样,我这写一个父组件而已,子组件一样:
constructor(props) { super(props)
this.state = {price: 0}
//先bind类中方法
this.changePrice = this.changePrice.bind(this)
}
之后在render的return要改成这样:
render() { return (
<div>
<Item changePrice={this.changePrice}/>
<p>{this.state.price}</p>
</div>
);
}
这是其中的一种作法,另一种作法可以参考我在另一篇回答: https://segmentfault.com/q/10...
2. 校正state(状态)里的资料,以及提升到父组件去
在子组件中的state(状态)中的资料是不是有那么必要放在子组件中,如果你还有第二个子组件、第三、第四…,它们都要用例如这里的选中资料,你放在这个子组件是违反了上面说的应用领域全局资料思维的。
先看一下子组件目前的state,是长这个样子:
this.state = { names: ['(1)免费行李','2','3'],
values: ['1','2','3'],
selectName: '',
prices: '0'
}
这里要先校正一下,names与values是代表选项中的名与值,它们是有关联的,所以应该是这样的下面结构才是好些的,value如果是要用来代表数字,就用数字就行不需要用字串:
options: [ {name:'(1)免费行李', value: 1 },
{name:'2', value: 2 },
{name:'3', value: 3 }
]
选中了哪个选项这个状态资料,还是要先放在子组件中,因为子组件中有选项盒,与触发的更动方法,但选项的资料可以移到上层的父组件中:
这是上层App.js中的状态:
this.state = { options: [
{name:'(1)免费行李', value: 1 },
{name:'2', value: 2 },
{name:'3', value: 3 }
],
price: 0
}
父组件也改用把state里面的选项值,用props值给子组件,所以在render里语句改成下面这样:
render() { return (
<div>
<Item changePrice={this.changePrice} optionArray={this.state.options}/>
<p>{this.state.price}</p>
</div>
)
}
子组件中这时可以用this.props.optionArray
接到传入的选项值,所以在render方法中,改用这个来代替之前的this.state.names与this.state.values,简单改写如下:
var options = [] var prices = this.state.prices
var optionArray = this.props.optionArray
for (var i = 0; i< optionArray.length; i++) {
options.push(
<option key={i} value={optionArray[i].value} >
{optionArray[i].name}
</option>
)
}
注: 这里不用
for...in
语句而用for语句,是因为for...in
语句是个不建议用在数组资料的语法,它并不会保证取到数组成员里的顺序。for...in
只会用在对象的寻遍中。
更精简的写法是用Array.map,如下:
var options = []var prices = this.state.prices
var optionArray = this.props.optionArray
options = optionArray.map(function(item, index){
return (
<option key={index} value={item.value} >
{item.name}
</option>
)
})
接著,如果依选项选中然后计算价格这件事,规划中应该是整个应用来作的,例如有可能还有其他的组件中也有其他的选项,最后统一要来算价格,所以计算价格这件事,也应该放到父组件去,所以如同上面的改写一样,把子组件的prices状态与相关计算的代码,都提到父组件,这个子组件纯用来当选项盒用而已。子组件此时连state都可以不用有。
因为整个改写过的代码会多些,所以我把父组件与子组件中的代码整个贴上。
父组件App.js:
import React, { Component } from 'react';import Item from './Item'
export default class App extends Component {
constructor(props) {
super(props)
this.state = {
options: [
{name:'(1)免费行李', value: 1 },
{name:'2', value: 2 },
{name:'3', value: 3 }
],
price: 0
}
this.changePrice = this.changePrice.bind(this)
}
changePrice(value){
var price = 800;
if(value === 1) price = 0
else price = (value - 1) * price
this.setState({price: price})
}
render() {
return (
<div>
<Item changePrice={this.changePrice} optionArray={this.state.options}/>
<p>{this.state.price}</p>
</div>
)
}
}
子组件Item.js:
import React, { Component } from 'react'export default class Item extends Component {
constructor(props) {
super(props)
this.handleChange = this.handleChange.bind(this)
}
handleChange(e){
this.props.changePrice(e.target.value)
}
render() {
var options = []
var optionArray = this.props.optionArray
options = optionArray.map(function(item, index){
return (
<option key={index} value={item.value} >
{item.name}
</option>
)
})
return (
<div>
<select onChange={this.handleChange}>
{options}
</select>
</div>
)
}
}
3. 目前最终进化版本
这个版本有几个改进如下,供参考:
用
let
/const
取代var
。不用分号(;)作为语句结尾。
Item子组件改用函数定义方式,取代原先的组件定义方式。
能合并的语句都合并。
函数全用箭头函数(注意需额外加装
babel-plugin-transform-class-properties
)。
App.js
import React, { Component } from 'react'import Item from './Item2'
export default class App extends Component {
constructor(props) {
super(props)
this.state = {
options: [
{name:'(1)免费行李', value: 1 },
{name:'2', value: 2 },
{name:'3', value: 3 }
],
price: 0
}
}
changePrice = (value) => {
let price = 800
if(value === 1) price = 0
else price *= value - 1
this.setState({price: price})
}
render() {
return (
<div>
<Item changePrice={this.changePrice} optionArray={this.state.options}/>
<p>{this.state.price}</p>
</div>
)
}
}
Item.js
import React from 'react'const Item = (props) => {
const optionArray = props.optionArray
const options = optionArray.map((item, index) => {
return (
<option key={index} value={item.value} >
{item.name}
</option>
)
})
return (
<div>
<select onChange={e => {props.changePrice(e.target.value)}}>
{options}
</select>
</div>
)
}
export default Item
可以用函数传值,大概是这样:
// 父组件class P extends Component {
fn(newState) {
this.setState({ someKey: newState });
}
render() {
return (
<Child pfn={this.fn} />
);
}
}
// 子组件
class Child extends Component {
someFn() {
this.props.pfn(newState); // 传给父组件了
}
render() {
return (
<div></div>
);
}
}
父组件获取子组件的值可以通过回调来完成
父组件
子组件
当然你也可以使用https://github.com/mroderick/... 发布订阅模式JS库
callback
ref
关于子组件向父组件传值,楼上的答案已经说明白了,那就是传一个方法进去,用callback的方式调用;
对于题主使用this.setState报错,除了像枯叶那样使用“var self = this”之外,还可以试试用bind:
constructor(props) { super(props);
this.state = { a: '' };
this.fn = this.fn.bind(this);
}
这样也能在fn()中使用this.setState而不会报错
以上是 【React】react 父组件怎么获取子组件的这个值? 的全部内容, 来源链接: utcz.com/a/71731.html