函数无法读取 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。在这种情况下发生的是 addItemuseEffect[=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>
  )
}