React 函数式组件使用props传参作为state的注意项
现实场景需求
UE4像素流推送前端插件存在一个bug, 就是无法在视频流上进行汉字的输入, 因为该插件本质是一个<video>
标签, 当然不能在其中使用输入法来键入中文, 但是在现实数字孪生开发环境中我们肯定需要进行汉字的输入来进行查询和修改, 那么我们如何来实现这个功能呢?
目前我想到了三种解决方案, 第一种是在UE4程序内集成输入法, 第二种是吧UWG放到前端页面来做, 第三种则是在进行中文输入插入光标时传递信息给UE4像素流前端, 前端因此弹出一个输入框, 输入完成后传送回UE4, 更新输入框.
目前来说第一种和第二种对我这种没有UE4基础和UI切图的人来说是不切实际的, 但是第三种还是可以实现的, 但是在我用函数式组件解决的过程中, 发现了一个BUG, 即React函数式组件使用props传参作为state时子组件useState只能获取到第一次的传递的值, 以后的值也不会更新state.
遇到的问题
通俗的说, 我想实现如下的效果, 我父组件想要传递一个是否显示输入对话框的boolean状态值, 初始值为false
, 当父组件收到要开始输入消息时, 将改值更新为true
, 子组件收到后弹出输入对话框, 然后输入文字内容, 发送给UE4.
我们现在用一个父组件button点击事件来模拟一下收到信息后发送给子组件的过程
父组件HandSendMsg核心代码:
import React, { useState } from 'react'
import InputModal from '../../components/InputModal';
export default function HandSendMsg() {
const [inputModalVisible, setInputModalVisible] = useState(false)
return (
<div>
<InputModal
emitMessageToUE4={emitMessageToUE4}
textContent={textContent}
inputModalVisible={inputModalVisible}
/>
<button
onClick={() => {
setTextContent("lalala")
setInputModalVisible(true)
}}
style={{
position: 'absolute',
zIndex: 100,
right: 0
}}
>test</button>
</div>
)
}
子组件InputModal核心代码:
import React, { useState } from 'react'
import { Input, Modal } from 'antd'
const { TextArea } = Input
export default function InputModal(props) {
const [inputModalVisible, setInputModalVisible] = useState(props.inputModalVisible)
const handleOk = () => {
// 发送消息给ue4, 代码省略
setInputModalVisible(false);
};
const handleCancel = () => {
// 取消发送消息
setInputModalVisible(false);
};
return (
<>
<Modal
centered
closable={false}
destroyOnClose
title={null}
visible={inputModalVisible}
onOk={handleOk}
onCancel={handleCancel}
cancelText="取消"
okText="确定"
>
<TextArea
showCount
maxLength={100}
placeholder="请输入内容"
allowClear
defaultValue={props.textContent}
/>
</Modal>
</>
)
}
如果是这样, 我们会发现在传递给子组件InputModal中的stateinputModalVisible
不会随着父组件的值改变而改变, 其值永远为false, 当我们点击父组件的button时, 并不会在子组件弹出modal对话框, 这是因为useState的原因而引起的, 后续props传参的改变不能引起useState钩子进行更新为true, 导致无法弹出对话框, 那么应该如何解决呢?
解决办法
使用redux来共同管理状态显然是可以解决的, redux中store\ reducers 和 actions代码再给出, 非常简单, 其维护了这个状态值, 给出父子组件代码:
父组件HandSendMsg核心代码:
import React, { useState } from 'react'
import InputModal from '../../components/InputModal';
// 引入useDispath用于分发数据
import { useDispatch } from "react-redux"
// 引入action
import { change_input_modal_visible } from "../../redux/actions/visible"
export default function HandSendMsg() {
const dispatch = useDispatch()
return (
<div>
<InputModal
emitMessageToUE4={emitMessageToUE4}
textContent={textContent}
/>
<button
onClick={() => {
setTextContent("lalala")
dispatch(change_input_modal_visible(true))
}}
style={{
position: 'absolute',
zIndex: 100,
right: 0
}}
>test</button>
</div>
)
}
子组件InputModal核心代码:
import React, {createRef} from 'react'
import { Input, Modal } from 'antd'
// 引入useSelector用于读取redux store中保存的数据, 引入useDispath用于分发数据
import { useSelector, useDispatch } from "react-redux"
// 引入action
import { change_input_modal_visible } from "../../redux/actions/visible"
const { TextArea } = Input
export default function InputModal(props) {
const dispatch = useDispatch()
// store里保存的数据
const inputModalVisible = useSelector(state => state.visible.inputModalVisible)
const handleOk = () => {
// 发送消息给ue4, 具体发送逻辑代码不再展示
dispatch(change_input_modal_visible(false));
};
const handleCancel = () => {
// 取消发送消息
dispatch(change_input_modal_visible(false));
};
return (
<>
<Modal
centered
closable={false}
destroyOnClose
title={null}
visible={inputModalVisible}
onOk={handleOk}
onCancel={handleCancel}
cancelText="取消"
okText="确定"
>
<TextArea
showCount
maxLength={100}
placeholder="请输入内容"
allowClear
defaultValue={props.textContent}
/>
</Modal>
</>
)
}