函数无法读取 useState 参数
Function can't read useState parameters
我正在使用 React Native 和 Firebase 实时数据库构建应用程序,当我启动函数 addItem
时,我从数据库获取的对象中的唯一参数是 id
,其他参数 return 为 ''
,我尝试使用 console.log()
并且 TextInputs 工作正常我也多次使用此配置,这是第一次发生。
export default function NewItem() {
const [name, setName] = useState('');
const [price, setPrice] = useState('');
const [description, setDescription] = useState('');
useEffect(() => {
navigation.setOptions({
headerRight: () => (
<TouchableOpacity onPress={addItem}>
<Text style={{fontWeight:'bold'}}>ADD ITEM</Text>
</TouchableOpacity>
)
})
}, [])
const addItem = () => {
const changes = ref(db, 'path')
get(changes).then( async (snapshot) => {
if (snapshot.val().data !== undefined) {
let array = []
let object = {
"id": `${id}`,
"name": name,
"price": price,
"description": description,
}
array.push(object)
update(changes, {
data: array
})
}
})
}
return (
<TouchableWithoutFeedback>
<TextInput
onChangeText={(e) => setName(e)}
/>
<TextInput
onChangeText={(e) => setShippingPrice(e)}
/>
<TextInput
onChangeText={(e) => setPrice(e)}
/>
<TextInput
onChangeText={(e) => setDescription(e)}
/>
</TouchableWithoutFeedback>
)
}
addItem
是它引用的状态的陈旧外壳,因为 useEffect
挂钩仅运行一次并在初始状态上关闭。您可以通过将 addItem
函数添加到依赖项数组来解决 re-enclosing 更新后的状态。
useEffect(() => {
navigation.setOptions({
headerRight: () => (
<TouchableOpacity onPress={addItem}>
<Text style={{fontWeight:'bold'}}>ADD ITEM</Text>
</TouchableOpacity>
)
})
}, [addItem]);
您应该记住 addItem
回调处理程序,以进一步减少 useEffect
钩子在 re-encloses 和 callback/state.
之上的次数
const addItem = React.useCallback(() => {
const changes = ref(db, 'path');
get(changes)
.then(async (snapshot) => {
if (snapshot.val().data !== undefined) {
const data = [{
"id": `${id}`,
name,
price,
description,
}];
update(changes, { data });
}
})
}, [name, price, description]);
或者,您可以将所有状态值缓存在 React ref 中,并通过回调中的 ref 访问它们。
示例:
export default function NewItem() {
const [name, setName] = useState('');
const [price, setPrice] = useState('');
const [description, setDescription] = useState('');
const stateRef = React.useRef({
name,
price,
description,
});
useEffect(() => {
stateRef.current = { name, price, description };
}, [name, price, description]);
useEffect(() => {
navigation.setOptions({
headerRight: () => (
<TouchableOpacity onPress={addItem}>
<Text style={{fontWeight:'bold'}}>ADD ITEM</Text>
</TouchableOpacity>
)
})
}, []);
const addItem = () => {
const changes = ref(db, 'path');
get(changes)
.then(async (snapshot) => {
if (snapshot.val().data !== undefined) {
const { name, price, description } = stateRef.current;
const data = [{
"id": `${id}`,
name,
price,
description,
}];
update(changes, { data });
}
})
}
return (...);
}
正如 Drew Reese 指出的那样,当您使用 JavaScript 闭包时,这是 React 中典型的 陈旧状态 问题.
useEffect 将函数作为参数,在该函数中你使用另一个函数 addItem 在 React Component 中声明并使用 React State。在这种情况下发生的是 addItem 在 useEffect[=38 时的条件下被记忆=]被执行了,就好像它的静态图片一样。不幸的是 addItem 不是纯函数,它依赖于 React 状态,所以它会在 React 生命周期中发生变化,而 useEffect回调版本将保持静态图片。发生这种情况是因为您初始化了 useEffect 挂钩,并使用一个空数组作为第二个参数 [].
这就是 React hooks 引入 dependencies 数组 的原因,该数组中的值决定了 hook 中的回调何时必须为 re-evaluated。有一个非常重要的 Linting 规则强制您将回调的所有依赖项放入 deps 数组中:@react-hooks/exhaustive-deps 以避免很多细微的错误和不当行为。
这应该是在您的案例中使用 React hooks 的正确方法,以提高性能并避免错误和不必要的重新渲染计算:
export default function NewItem() {
const [name, setName] = useState('');
const [price, setPrice] = useState('');
const [description, setDescription] = useState('');
useEffect(() => {
navigation.setOptions({
headerRight: () => (
<TouchableOpacity onPress={addItem}>
<Text style={{fontWeight:'bold'}}>ADD ITEM</Text>
</TouchableOpacity>
)
})
}, [addItem])
const addItem = useCallback(() => {
const changes = ref(db, 'path')
get(changes).then( async (snapshot) => {
if (snapshot.val().data !== undefined) {
const array = []
const object = {
"id": `${id}`,
"name": name,
"price": price,
"description": description,
}
array.push(object)
update(changes, {
data: array
})
}
})
},[name, price, description ])
return (
<TouchableWithoutFeedback>
<TextInput
onChangeText={(e) => setName(e)}
/>
<TextInput
onChangeText={(e) => setShippingPrice(e)}
/>
<TextInput
onChangeText={(e) => setPrice(e)}
/>
<TextInput
onChangeText={(e) => setDescription(e)}
/>
</TouchableWithoutFeedback>
)
}
我正在使用 React Native 和 Firebase 实时数据库构建应用程序,当我启动函数 addItem
时,我从数据库获取的对象中的唯一参数是 id
,其他参数 return 为 ''
,我尝试使用 console.log()
并且 TextInputs 工作正常我也多次使用此配置,这是第一次发生。
export default function NewItem() {
const [name, setName] = useState('');
const [price, setPrice] = useState('');
const [description, setDescription] = useState('');
useEffect(() => {
navigation.setOptions({
headerRight: () => (
<TouchableOpacity onPress={addItem}>
<Text style={{fontWeight:'bold'}}>ADD ITEM</Text>
</TouchableOpacity>
)
})
}, [])
const addItem = () => {
const changes = ref(db, 'path')
get(changes).then( async (snapshot) => {
if (snapshot.val().data !== undefined) {
let array = []
let object = {
"id": `${id}`,
"name": name,
"price": price,
"description": description,
}
array.push(object)
update(changes, {
data: array
})
}
})
}
return (
<TouchableWithoutFeedback>
<TextInput
onChangeText={(e) => setName(e)}
/>
<TextInput
onChangeText={(e) => setShippingPrice(e)}
/>
<TextInput
onChangeText={(e) => setPrice(e)}
/>
<TextInput
onChangeText={(e) => setDescription(e)}
/>
</TouchableWithoutFeedback>
)
}
addItem
是它引用的状态的陈旧外壳,因为 useEffect
挂钩仅运行一次并在初始状态上关闭。您可以通过将 addItem
函数添加到依赖项数组来解决 re-enclosing 更新后的状态。
useEffect(() => {
navigation.setOptions({
headerRight: () => (
<TouchableOpacity onPress={addItem}>
<Text style={{fontWeight:'bold'}}>ADD ITEM</Text>
</TouchableOpacity>
)
})
}, [addItem]);
您应该记住 addItem
回调处理程序,以进一步减少 useEffect
钩子在 re-encloses 和 callback/state.
const addItem = React.useCallback(() => {
const changes = ref(db, 'path');
get(changes)
.then(async (snapshot) => {
if (snapshot.val().data !== undefined) {
const data = [{
"id": `${id}`,
name,
price,
description,
}];
update(changes, { data });
}
})
}, [name, price, description]);
或者,您可以将所有状态值缓存在 React ref 中,并通过回调中的 ref 访问它们。
示例:
export default function NewItem() {
const [name, setName] = useState('');
const [price, setPrice] = useState('');
const [description, setDescription] = useState('');
const stateRef = React.useRef({
name,
price,
description,
});
useEffect(() => {
stateRef.current = { name, price, description };
}, [name, price, description]);
useEffect(() => {
navigation.setOptions({
headerRight: () => (
<TouchableOpacity onPress={addItem}>
<Text style={{fontWeight:'bold'}}>ADD ITEM</Text>
</TouchableOpacity>
)
})
}, []);
const addItem = () => {
const changes = ref(db, 'path');
get(changes)
.then(async (snapshot) => {
if (snapshot.val().data !== undefined) {
const { name, price, description } = stateRef.current;
const data = [{
"id": `${id}`,
name,
price,
description,
}];
update(changes, { data });
}
})
}
return (...);
}
正如 Drew Reese 指出的那样,当您使用 JavaScript 闭包时,这是 React 中典型的 陈旧状态 问题. useEffect 将函数作为参数,在该函数中你使用另一个函数 addItem 在 React Component 中声明并使用 React State。在这种情况下发生的是 addItem 在 useEffect[=38 时的条件下被记忆=]被执行了,就好像它的静态图片一样。不幸的是 addItem 不是纯函数,它依赖于 React 状态,所以它会在 React 生命周期中发生变化,而 useEffect回调版本将保持静态图片。发生这种情况是因为您初始化了 useEffect 挂钩,并使用一个空数组作为第二个参数 [].
这就是 React hooks 引入 dependencies 数组 的原因,该数组中的值决定了 hook 中的回调何时必须为 re-evaluated。有一个非常重要的 Linting 规则强制您将回调的所有依赖项放入 deps 数组中:@react-hooks/exhaustive-deps 以避免很多细微的错误和不当行为。
这应该是在您的案例中使用 React hooks 的正确方法,以提高性能并避免错误和不必要的重新渲染计算:
export default function NewItem() {
const [name, setName] = useState('');
const [price, setPrice] = useState('');
const [description, setDescription] = useState('');
useEffect(() => {
navigation.setOptions({
headerRight: () => (
<TouchableOpacity onPress={addItem}>
<Text style={{fontWeight:'bold'}}>ADD ITEM</Text>
</TouchableOpacity>
)
})
}, [addItem])
const addItem = useCallback(() => {
const changes = ref(db, 'path')
get(changes).then( async (snapshot) => {
if (snapshot.val().data !== undefined) {
const array = []
const object = {
"id": `${id}`,
"name": name,
"price": price,
"description": description,
}
array.push(object)
update(changes, {
data: array
})
}
})
},[name, price, description ])
return (
<TouchableWithoutFeedback>
<TextInput
onChangeText={(e) => setName(e)}
/>
<TextInput
onChangeText={(e) => setShippingPrice(e)}
/>
<TextInput
onChangeText={(e) => setPrice(e)}
/>
<TextInput
onChangeText={(e) => setDescription(e)}
/>
</TouchableWithoutFeedback>
)
}