问题描述
标题有点罗word。我的意思是我有一个子组件(这是一个功能组件)和一个父类。但是,该父类不会返回JSX,因此,我不能使用hooks / useRef()
。由于出现此错误,我也不能仅向子组件添加引用:
Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?
这是子类,其中有一些代码被剥离:
const BluetoothDeviceItem = (Props) => {
const [connectingState,setConnectingState] = useState(Props.state());
useEffect(() => {
stateChangeCallback();
},[connectingState]);
const { onClick } = Props;
const stateChangeCallback = () => {
switch (connectingState) {
case 'not_connected':
//stuff
break;
case 'connecting':
//stuff
break;
case 'connected':
//stuff
break;
}
}
const rerender = () => {
setConnectingState(Props.state());
}
const wait = async () => {
onClick(Props.mac);
rerender();
}
return (
<IonItem lines="none" class="item-container" id={Props.mac} onClick={wait} ref={Props.elRef}>
<IonAvatar slot="start" class="bluetooth-icon-container">
<IonIcon icon={bluetoothOutline} class="bluetooth-icon"></IonIcon>
</IonAvatar>
<IonAvatar slot="end" class="connecting-icons-container">
<IonIcon icon={cloSEOutline} class="connecting-icons"></IonIcon>
<IonSpinner name="dots" class="connecting-spinner"></IonSpinner>
</IonAvatar>
<IonLabel>
<div className="device-name">{Props.name}</div>
<div className="device-mac">{Props.mac}</div>
</IonLabel>
</IonItem>
);
}
export default BluetoothDeviceItem;
这是父母(再次打扫一点):
const _Bluetooth = () => {
let states: {} = {}
const deviceItems: any[] = [];
const deviceRefs: any[] = [];
const bluetoothInitialize = () => {
for (let i = 0; i < 5; i++) {
let a = {name: "test_"+i,mac: i.toString(),connected: false}
createDeviceItem(a);
}
}
const connect = (id) => {
deviceItems.forEach(item => { //disconnecting any other currently connected devices and changing their respective states
if ((item.props.state() === 'connecting' || item.props.state() === 'connected') && item.props.mac !== id) {
updateDeviceItemState(item.props.mac,'not_connected');
}
if(id === item.props.mac) {
updateDeviceItemState(item.props.mac,'connecting');
}
});
}
const createDeviceItem = (data) => {
states = {...states,[data.mac]: data.connected ? 'connected' : 'not_connected'}
let ref = createRef();
deviceItems.push(<BluetoothDeviceItem elRef={ref} key={data.mac} mac={data.mac} name={data.name} onClick={(id) => connect(id)} state={() => {return states[data.mac]}} elRef={ref}></BluetoothDeviceItem>);
deviceRefs.push(ref);
}
const updateDeviceItemState = (id,connectingState) => {
states[id] = connectingState;
}
return (
{deviceItems,bluetoothInitialize}
);
}
export default _Bluetooth;
父类进行蓝牙扫描(目前仅被for循环替换)。每次找到设备时,它都会创建一个新的BluetoothDeviceItem。当您单击一个时,它的状态设置为not_connected
。同时,它会检查当前是否还有其他设备connecting
或connected
。
现在,将设备设置为connecting
可以正常工作,因为在单击设备时,它将调用wait
函数,该函数将更新BluetoothDeviceItem的状态。但是,在更新所有其他设备的状态时(检查它们是否为connected
或connecting
之后),因为它们不是通过onClick调用的,所以它们不会意识到其状态已更新。这就是为什么我实现了rerender
函数的原因,该函数将在每个BluetoothDeviceItem上调用以确保它们具有最新状态。
问题是我需要提升此功能,以便父类可以访问它。最明显的想法是使用引用。创建一个引用,将其附加到BluetoothDeviceItem组件并获取功能。当然这样做会产生我上面所说的错误。
第二个最好的方法是将ref作为props传递并将其附加到DOM元素。不幸的是,这意味着我无法访问这些函数,因为它们在DOM元素上不存在。
我尝试的最后一件事是使用传播操作符(...)传播重新渲染功能,如下所示:
<IonItem lines="none" class="item-container" id={Props.mac} onClick={wait} ref={Props.elRef}> {...rerender}
因此我可以使用ref访问它。这是行不通的,因为... rerender的求值为{}(在某个时间点我可以展开一个函数,但我不记得怎么做)。
当状态改变时,如何将此重新渲染功能提升到父级或更新BluetoothDeviceItem?
这类似于我的previous post,但实际上我正在使用提供的答案并将所有状态存储在父类中。我认为,由于我需要这些类和组件的工作方式的性质,总会需要从父类的子组件中访问某些内容,同时也有严格的限制,例如子组件是函数组件还是父组件不是返回JSX
解决方法
我真的找不到简单的方法来做,所以我最终只是将BluetoothDeviceItem函数组件转换为React组件,就像这样:
class BluetoothDeviceItem extends React.Component<Props,{}> {
constructor(props) {
super(props);
}
private updateIcons = () => {
switch (this.props.state()) {
case 'not_connected':
break;
case 'connecting':
break;
case 'connected':
break;
}
}
public rerender = () => {
this.updateIcons();
}
private wait = async () => {
this.props.onClick(this.props.mac);
this.rerender();
}
render() {
return (
<IonItem lines="none" class="item-container" id={this.props.mac} onClick={this.wait}>
<IonAvatar slot="start" class="bluetooth-icon-container">
<IonIcon icon={bluetoothOutline} class="bluetooth-icon"></IonIcon>
</IonAvatar>
<IonAvatar slot="end" class="connecting-icons-container">
<IonIcon icon={closeOutline} class="connecting-icons"></IonIcon>
<IonSpinner name="dots" class="connecting-spinner"></IonSpinner>
</IonAvatar>
<IonLabel>
<div className="device-name">{this.props.name}</div>
<div className="device-mac">{this.props.mac}</div>
</IonLabel>
</IonItem>
);
}
}
export default BluetoothDeviceItem;
,然后在该组件上创建实例时仅使用引用。这使我可以访问所需的功能。