我在其中一个组件中遇到一个奇怪的问题,即在事件处理程序函数中道具和局部状态均未显示。
export default function KeyboardState({layout, children}) {
// Setup local component state
const [contentHeight, setContentHeight] = useState(layout.height)
const [keyboardHeight, setKeyboardHeight] = useState(0)
const [keyboardVisible, setKeyboardVisible] = useState(false)
const [keyboardWillShow, setKeyboardWillShow] = useState(false)
const [keyboardWillHide, setKeyboardWillHide] = useState(false)
const [keyboardAnimationDuration, setKeyboardAnimationDuration] = useState(INITIAL_ANIMATION_DURATION)
// set up event listeners
useEffect(()=> {
let subscriptions = []
if (Platform.OS === 'ios') {
subscriptions = [
Keyboard.addListener('keyboardWillShow', keyboardWillShowHandler),
// other listeners
]
}
return ()=> subscriptions.forEach(subscription => subscription.remove())
}, [])
// ODD BEHAVIOR
console.log(layout) // object: {height: 852, width: 414, x: 0, y: 44}
console.log(contentHeight) // 550
const keyboardWillShowHandler = (event) => {
console.log(layout) // object: {height: 0, width: 0, x: 0, y: 0}
console.log(contentHeight) // 0
setKeyboardWillShow(true)
measureEvent(event)
}
可能是什么原因导致这种情况发生?在事件处理程序中,我们应该可以访问本地组件的props和state,因此我想不出一个解释。
我已经确认此组件在此期间未收到更新的布局道具(通过使用useEffect并将布局作为要依赖的依赖传递)。对我来说,特别奇怪的是,事件处理程序中的日志记录仅在值为0的情况下为布局提供了正确的对象结构,而传入的布局属性未更改。
修改:
看来问题出在我如何初始化返回布局的自定义挂钩。我一直在将其初始化为具有高度,宽度,x和y属性为0的对象。具体于设备的解决方案是将其初始化为具有与iPhone X尺寸匹配的值的对象。但是我该怎么办?动态地实现这一目标?当组件未检测到新的传入prop值时,为什么在事件处理程序的KeyboardState中,布局又变回初始状态?
/*UseComponentSize.js (custom hook) */
const useComponentSize = (initial = initialState) => {
const [layout, setLayout] = useState(initial)
const onLayout = useCallback(event => {
console.log(event)
const newLayout = {...event.nativeEvent.layout}
newLayout.y = newLayout.y + (Platform.OS === 'android' ? Constants.statusBarHeight : 0)
setLayout(newLayout)
}, [])
return [layout, onLayout]
}
/*App.js*/
const [layout, onLayout] = useComponentSize()
return (
<View style={styles.container}>
<View style={styles.layout} onLayout={onLayout}>
<KeyboardState layout={layout}>
...
</KeyboardState>
</View>
</View>
)
编辑2:
我通过将自定义钩子中的布局初始化为null来解决此问题,同时修改了App.js以在布局不为null的情况下返回。那是:
/*App.js*/
const [layout, onLayout] = useComponentSize()
return (
<View style={styles.container}>
<View style={styles.layout} onLayout={onLayout}>
{layout && (
<KeyboardState layout={layout}>
...
</KeyboardState>
)}
</View>
</View>
)
我仍然不知道为什么我的KeyboardState组件在事件处理程序外正确返回布局属性,而在事件处理程序内调用布局属性时却返回useComponentSize()初始状态。
答案 0 :(得分:0)
我的处理程序函数引用的是陈旧的值,而不是更新的状态。
之所以per this question,是因为我的处理程序属于渲染定义的位置,因此后续的重渲染都不会更改事件侦听器,因此它仍从其渲染中读取旧值。
通过添加一个引用来跟踪布局状态的更改来解决此问题:
export default const useKeyboard = () => {
const [layout, _setLayout] = useState(null)
const layoutRef = useRef(layout) // initially null
const [contentHeight, setContentHeight] = useState(null)
const setLayout = (value) => {
layoutRef.current = value // keep ref update
_setLayout(value) // then call setState
}
// onLayout function applied to onLayout prop of view component within App.js
const onLayout = useCallback(event => {
const newLayout = {...event.nativeEvent.layout}
newLayout.y = newLayout.y + (Platform.OS === 'android' ? Constants.statusBarHeight : 0)
setLayout(newLayout) // calls modified setLayout function
setContentHeight(newLayout.height)
}, [])
useEffect(()=> {
Keyboard.addListener('keyboardWillShow', keyboardWillShowHandler)
// cleanup function not shown for brevity
}, [])
const keyboardWillShowHandler = (event) => {
setKeyboardWillShow(true)
measureEvent(event)
}
const measureEvent = (event) => {
const { endCoordinates: {height, screenY}, duration = INITIAL_ANIMATION_DURATION} = event
setContentHeight(screenY - layoutRef.current.y) // access ref value
}
return {layout: layoutRef.current, onLayout, contentHeight}
}