ReactJS中将CamSelect选中值传递给VideoStream实现视频切换
问题原因
- localStorage的变更不会触发React组件重渲染,VideoStream组件仅在首次渲染时读取一次localStorage的值,后续localStorage更新后组件感知不到,不会重新拉取url渲染播放器。
- CamSelect组件中
setState是异步操作,原代码在componentDidMount里调用setState后立刻读取this.state.selected拿到的还是更新前的空值,写入localStorage的初始值本身就是错误的。
解决方案
采用状态提升实现组件通信,是当前场景下成本最低的实现方式:将选中的摄像头流地址状态放到两个组件的公共父组件中维护,通过props传递状态和更新回调,实现状态变更的同步响应。
步骤1:修改公共父组件(示例为App.js,即CamSelect和VideoStream的共同上层组件)
import React, { useState } from 'react'; import CamSelect from './CamSelect'; import VideoStream from './VideoStream'; function App() { // 公共状态存储选中的摄像头流地址 const [selectedCamUrl, setSelectedCamUrl] = useState(''); return ( <div className="App"> {/* 把更新url的方法传给CamSelect */} <CamSelect onUrlChange={setSelectedCamUrl} /> {/* 把当前选中的url传给VideoStream */} <VideoStream url={selectedCamUrl} /> </div> ); } export default App;
步骤2:修改CamSelect.js
移除内部状态的单独维护,切换/初始化选项时通过props回调通知父组件更新公共状态:
import React from "react"; import { Select, MenuItem, FormControl, InputLabel } from "@material-ui/core"; import "./CamSelect.css" export default class CamSelect extends React.Component { constructor(props) { super(props); this.state = { currentCity: localStorage.getItem("city"), data: [ { location: "abc", link: [ {name: "cam1", url: "https://...."}, {name: "cam2", url: "https://...."} ] }, { location: "xyz", link: [ {name: "cam1", url: "https://...."}, ] }, ], selected: '', }; } componentDidMount () { this.state.data.map(ur => { if(this.state.currentCity == ur.location){ const defaultUrl = ur.link[0].url; this.setState({selected: defaultUrl}) // 初始化时把默认url同步给父组件 this.props.onUrlChange(defaultUrl); } }) } handleChange = (event) => { const newUrl = event.target.value; this.setState({ selected: newUrl, name: event.target.name }); // 切换选项时把新url同步给父组件 this.props.onUrlChange(newUrl); }; renderOptions() { return this.state.data.map((dt, i) => { if(this.state.currentCity == dt.location){ return dt.link.map((lnk, index) => { return ( <MenuItem label="Select a camera" value={lnk.url} key={index} name={lnk.name} > {lnk.name} </MenuItem> ) }) } } ) }; render() { return ( <FormControl className="camera-select" style={{ minWidth: "100px"}}> <InputLabel id="demo-simple-select-label" style={{color:"white"}}>Camera</InputLabel> <Select labelId="demo-simple-select-label" id="demo-simple-select" className="camera-text-color" value={this.state.selected} onChange={this.handleChange} > {this.renderOptions()} </Select> </FormControl> ); } }
步骤3:修改VideoStream.js
直接读取props传入的url即可,props变化会自动触发组件重渲染,更新播放器地址:
import React from 'react'; import ReactPlayer from 'react-player'; import { Card, CardContent } from '@material-ui/core'; import "./VideoStream.css" // 接收父组件传入的url参数 const VideoStream = ({url}) => { return ( <Card className="video-player"> <label className="video-label">Live Feed </label> <CardContent className="video-cardContent"> <ReactPlayer url= {url} width="100%" height="250px" playing={true} muted={true} controls className="hls-player" /> </CardContent> </Card> ); }; export default VideoStream;
如果两个组件的层级相差很大,逐层传递props比较繁琐,也可以用React Context来共享选中的url状态,不需要额外引入第三方状态管理库。
内容的提问来源于stack exchange,提问作者Vishal K.
相关产品推荐
相关产品推荐

