javascript - 1 组件的所有实例都引用第一个实例而不是其自身;2 子道具只有在调用父母的 setState 两次后才会更新
问题描述
更新:我通过为所有“评级”组件提供唯一名称轻松解决了第一个问题,但关于 2 x setState 修复道具的问题仍然悬而未决。
我其实有两个问题。第二个来自试图解决第一个问题。最初的问题是,每个 SongScoring 组件中的每个 onChange Rating 属性中的“this”都指向 SongScoring 类的第一个实例,而不是它本身:
父代码:
import React, { Component } from 'react';
import './Assess.css';
import { Accordion } from 'react-bootstrap';
import SongScoring from './SongScoring';
class Assess extends Component {
constructor(props) {
super(props);
const songs = [
{
track: "song1",
},
{
track: "song2",
},
{
track: "song3",
},
{
track: "song4",
},
]
this.state = {
songs: songs,
};
}
render() {
return (
<div className="root">
<Accordion defaultActiveKey="0">
{
this.state.songs.map((song, index) => (
<SongScoring song={song} key={index.toString()} index={index} />
))
}
</Accordion>
</div>
);
}
}
export default Assess;
子代码:
import React, { Component } from 'react';
import './SongScoring.css';
import { Accordion, Card, Container, Row } from 'react-bootstrap';
import ReactPlayer from "react-player";
import Rating from '@material-ui/lab/Rating';
import Box from '@material-ui/core/Box';
const labels = {
0.5: 'Unpleasant',
1: 'Bearable',
1.5: 'Bearable+',
2: 'Intriguing',
2.5: 'Intriguing+',
3: 'Ok',
3.5: 'Ok+',
4: 'Pleasant',
4.5: 'Pleasant+',
5: 'Excellent',
};
class SongScoring extends Component {
constructor(props) {
super(props);
this.state = {
song: props.song,
key: props.index.toString(),
score: props.song.score || 0,
hover: props.song.score || -1,
onChange: this.props.onChange,
onChangeActive: this.props.onChangeActive
}
}
render() {
return (
<>
<Card>
<Accordion.Toggle as={Card.Header} variant="link" eventKey={this.state.key}>
{this.state.key}
<Rating name="read-only" value={this.state.score} precision={0.5} readOnly />
</Accordion.Toggle>
<Accordion.Collapse eventKey={this.state.key}>
<Card.Body style={{ display: 'flex', alignItems: 'center', lineHeight: '1' }}>
<Container>
<Row className='scoring-row'>
<ReactPlayer
url="https://file-examples-com.github.io/uploads/2017/11/file_example_MP3_700KB.mp3"
width="400px"
height="50px"
playing={false}
controls={true}
style={{ outline: 'none' }}
/>
</Row>
<Row className='scoring-row'>
<Rating
name="song-rating"
value={this.state.score}
precision={0.5}
onChange={(event, newScore) => {
this.setState({
score: newScore
});
}}
onChangeActive={(event, newHover) => {
this.setState({
hover: newHover
});
}}
style={{ marginTop: '40px' }}
/>
</Row>
<Row className='scoring-row'>
{this.state.score !== 0 && <Box>{labels[this.state.hover !== -1 ? this.state.hover : this.state.score]}</Box>}
</Row>
</Container>
</Card.Body>
</Accordion.Collapse>
</Card>
</>
);
}
}
export default SongScoring;
经过多次迭代尝试通过从箭头函数和绑定更改来解决这个问题,将 onChange 和 onChangeActive 放入状态等。我最终决定尝试通过将 onChange 和 onChangeActive 函数从父级传递给子级来解决这个问题。
这就是我想出的(在 onChange 和 onChangeActive 中有 console.log 而不是 setState,但我们真正关心的是 'this' 引用):
父代码:
import React, { Component } from 'react';
import './Assess.css';
import { Accordion } from 'react-bootstrap';
import SongScoring from './SongScoring';
class Assess extends Component {
constructor(props) {
super(props);
const songs = [
{
track: "song1",
},
{
track: "song2",
},
{
track: "song3",
},
{
track: "song4",
},
]
this.songScoring = [];
const onChange = [];
const onChangeActive = [];
for (let i = 0; i < songs.length; i++) {
this.songScoring.push(React.createRef());
onChange.push(function (event, newScore) {
console.log("onChange: ", this);
});
onChangeActive.push(function (event, newHover) {
console.log("onChangeActive: ", this);
});
}
this.state = {
songs: songs,
onChange: onChange,
onChangeActive: onChangeActive
};
}
componentDidMount() {
console.log(this.state.songScoring);
const onChange = [];
const onChangeActive = [];
for (let i = 0; i < this.state.songs.length; i++) {
onChange.push(this.state.onChange[i].bind(this.songScoring[i], 1));
onChangeActive.push(this.state.onChangeActive[i].bind(this.songScoring[i]));
}
this.setState({
onChange: onChange,
onChangeActive: onChangeActive
});//, () => this.setState({unicorn: 1}));
}
render() {
return (
<div className="root">
<Accordion defaultActiveKey="0">
{
this.state.songs.map((song, index) => (
<SongScoring song={song} ref={this.songScoring[index]} key={index.toString()} index={index} onChange={this.state.onChange[index]} onChangeActive={this.state.onChangeActive[index]} />
))
}
</Accordion>
</div>
);
}
}
export default Assess;
子代码:
import React, { Component } from 'react';
import './SongScoring.css';
import { Accordion, Card, Container, Row } from 'react-bootstrap';
import ReactPlayer from "react-player";
import Rating from '@material-ui/lab/Rating';
import Box from '@material-ui/core/Box';
const labels = {
0.5: 'Unpleasant',
1: 'Bearable',
1.5: 'Bearable+',
2: 'Intriguing',
2.5: 'Intriguing+',
3: 'Ok',
3.5: 'Ok+',
4: 'Pleasant',
4.5: 'Pleasant+',
5: 'Excellent',
};
class SongScoring extends Component {
constructor(props) {
super(props);
this.state = {
song: props.song,
key: props.index.toString(),
score: props.song.score || 0,
hover: props.song.score || -1,
onChange: this.props.onChange,
onChangeActive: this.props.onChangeActive
}
}
componentDidUpdate(prevProps, prevState){
if (prevProps.onChange !== this.state.onChange){
this.setState({
onChange: prevProps.onChange,
onChangeActive: prevProps.onChangeActive
})
}
}
render() {
return (
<>
<Card>
<Accordion.Toggle as={Card.Header} variant="link" eventKey={this.state.key}>
{this.state.key}
<Rating name="read-only" value={this.state.score} precision={0.5} readOnly />
</Accordion.Toggle>
<Accordion.Collapse eventKey={this.state.key}>
<Card.Body style={{ display: 'flex', alignItems: 'center', lineHeight: '1' }}>
<Container>
<Row className='scoring-row'>
<ReactPlayer
url="https://file-examples-com.github.io/uploads/2017/11/file_example_MP3_700KB.mp3"
width="400px"
height="50px"
playing={false}
controls={true}
style={{outline: 'none'}}
/>
</Row>
<Row className='scoring-row'>
<Rating
name="song-rating"
value={this.state.score}
precision={0.5}
onChange={this.state.onChange}
onChangeActive={this.state.onChangeActive}
style={{ marginTop: '40px' }}
/>
</Row>
<Row className='scoring-row'>
{this.state.score !== 0 && <Box>{labels[this.state.hover !== -1 ? this.state.hover : this.state.score]}</Box>}
</Row>
</Container>
</Card.Body>
</Accordion.Collapse>
</Card>
</>
);
}
}
export default SongScoring;
在此设置中,console.log 中的 onChange 和 onChangeActive 都输出“未定义”。
现在,如果我取消注释片段:'//, () => this.setState({unicorn: 1}));' (独角兽是一个虚拟的、未使用的变量)在父项中,onChange 和 onChangeActive 的 console.log 很好地打印出“this”作为各个 SongScoring 组件的引用。
因此我的问题是:
- 在这两个问题中,这里发生了什么?它是“评级”组件的错误吗?
- 如何比使用带有未注释片段的第二种方法更有效地解决我的初始问题?
更新:我通过为所有“评级”组件提供唯一名称轻松解决了第一个问题,但关于 2 x setState 修复道具的问题仍然悬而未决。
解决方案
推荐阅读
- python - 从它包含的多个实例中表示一个包的方法是什么?
- fabricjs - fabric.js - 向形状添加自定义属性
- sql - 我希望 CTE 中的帮助显示表值中的标题名称
- encryption - 如何在可能来自不同设备且服务器不存储任何加密密钥的客户端上加密和解密?
- python - Perform row multiplication in data frame
- naming-conventions - 如何使用方法 register() 命名一个类?注册人/注册人/注册人?
- javascript - JavaScript 不写在页面上
- mongodb - NodeJS MongoDb $sort 在聚合查询中不起作用
- security - API 中的正确身份验证策略
- python - TestCase 和 mixin 类的 setUp()/tearDown() 方法的继承顺序