【问题标题】:React Native: TextInput with state and AsyncStorageReact Native:带有状态和 AsyncStorage 的 TextInput
【发布时间】:2018-05-08 12:47:54
【问题描述】:

当我在键盘上打字时,我看到一些关于输入在 JS 代码之前的警告..

Native TextInput(react native 很棒)比 JS 提前 4 个事件 - 尝试让您的 JS 更快。

所以添加了debounce 并让它“工作”:

...
import { debounce } from 'lodash'
...
export default class App extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      data,
      indexRef: data.reduce((result, item, index) => {
        result[item.title] = index
        return result
      }, {}),
      ready: false,
    }

    this.updatePitch = this.updatePitch.bind(this)
    this.saveLocally = debounce(this.saveLocally, 300).bind(this)
  }
  ...
  updatePitch(id, text) {
    // Copy the data
    let data = [...this.state.data]
    const index = data.findIndex(obj => obj.id == id)
    data[index].pitch = text
    // Update the state
    this.setState({ data }, this.saveLocally(data))
  }

  saveLocally(data) {
    try {
      AsyncStorage.setItem('data', JSON.stringify(data))
      this.forceUpdate()
    } catch (error) {
      // Well..
    }
  }

  render() {
  ...

顺便说一句:我现在正在做一些“道具钻探” - 但计划使用Context API (react 16.3)

警告似乎已经通过添加 debounce (1).. 但我看到了一些奇怪的问题 - 特别是在 iPhone 8 plus 模拟器上(在 iPhone 6 模拟器或 Android 设备上看不到相同的问题)

观察到的问题:

  • TextInput 不要展开 - 它只是添加 scolling(在 iPhone 6 和 Android 设备上展开)
  • FlatList 中的一些布局问题 - 似乎无法找到正确的列表元素高度..

快速 JS 代码并同时保存到 stateAsyncStorage 的最佳做法是什么?

(1) 使用debounce 之外的另一种方法可能是使用getDerivedStateFromProps 并添加某种计时器,在一段时间后将状态推送到父组件。但不确定这是否会使JS代码更快。所以没试过。

更新(再次)

我开源了整个代码,因为当代码如此嵌套时,很难在 SO 帖子中提供所有需要的信息。

完整的代码在这里: https://github.com/Norfeldt/LionFood_FrontEnd

(我知道我的代码可能看起来很乱,但我仍在学习..)

我不希望人们进入并使用 PR 修复我的代码(即使它会很棒)但只是给我一些关于如何正确处理 stateAsyncStorage for TextInput 的代码指导.

我知道我有一些风格问题 - 很想解决它们,但也遵守 SO 并保持主题。

更新二

我删除了 forceUpdate 并将 FadeImage 替换为 vanilla react native Image

但我仍然看到一些奇怪的问题

这是我的代码

import React from 'react'
import {
  StyleSheet,
  SafeAreaView,
  FlatList,
  StatusBar,
  ImageBackground,
  AsyncStorage,
  Platform,
} from 'react-native'
import SplashScreen from 'react-native-splash-screen'
import LinearGradient from 'react-native-linear-gradient'
import { debounce } from 'lodash'

import Section from './Section'
import ButtonContact from './ButtonContact'

import { data } from '../data.json'

export default class App extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      data,
      indexRef: data.reduce((result, item, index) => {
        result[item.title] = index
        return result
      }, {}),
      ready: false,
    }
  }

  async componentDidMount() {
    SplashScreen.hide()
    try {
      let BusinessPlan = await AsyncStorage.getItem('BusinessPlan')
      if (BusinessPlan !== null) {
        // We have data!!
        let data = JSON.parse(BusinessPlan)
        data = this.state.data.map(item => {
          const index = data.findIndex(obj => obj.id == item.id)
          const pitch = index >= 0 ? data[index].pitch : ''
          return { ...item, pitch }
        })
        this.setState({ data, ready: true })
      } else {
        this.setState({ ready: true })
      }
    } catch (error) {
      // Error retrieving data
    }
  }

  updatePitch = (id, text) => {
    // Copy the data
    let data = [...this.state.data]
    const index = data.findIndex(obj => obj.id == id)
    data[index].pitch = text
    // Update the state
    this.setState({ data }, this.saveLocally(data))
  }

  saveLocally = data => {
    try {
      AsyncStorage.setItem('BusinessPlan', JSON.stringify(data))
    } catch (error) {
      // Well..
    }
  }

  render() {
    return (
      <LinearGradient
        style={{ flex: 1 }}
        start={{ x: 0.0, y: 0.25 }}
        end={{ x: 0.5, y: 1.0 }}
        colors={['#000000', '#808080', '#000000']}
      >
        <StatusBar
          barStyle={'light-content'}
          backgroundColor={Platform.OS == 'iOS' ? 'transparent' : 'black'}
        />
        <SafeAreaView>
          <ImageBackground
            source={require('../images/BackgroundImage.png')}
            style={{ width: '100%', height: '100%' }}
            resizeMode={'cover'}
          >
            <FlatList
              data={this.state.data}
              initialNumToRender="16"
              keyExtractor={item => item.id}
              renderItem={({ item }) => (
                <Section
                  id={item.id}
                  title={item.title}
                  pitch={item.pitch}
                  updatePitch={debounce(this.updatePitch, 1000)}
                  questions={item.questions}
                  ready={this.state.ready}
                />
              )}
              ListFooterComponent={<ButtonContact />}
              style={{
                backgroundColor: 'transparent',
                borderColor: '#000',
                borderWidth: StyleSheet.hairlineWidth,
              }}
            />
          </ImageBackground>
        </SafeAreaView>
      </LinearGradient>
    )
  }
}

const styles = StyleSheet.create({
  sectionHeader: {
    fontSize: 24,
    marginHorizontal: 5,
  },
})

(我也更新了我的 git repo)

更新 III

我为stateAsyncStorage 设置的设置似乎与debounce 配合得很好。我看到的问题是因为我正在耗尽 CPU(下一步要修复)。

【问题讨论】:

  • TextInput 不需要去抖动。你为什么打电话给this.forceUpdate()?尝试删除它并去抖动。
  • @riwu 我强制更新它,因为我在使用 textinput 或 flatlist 时遇到了一些布局问题(不记得)
  • 那么您应该尝试修复这些布局问题。在这里调用this.forceUpdate() 是绝对错误的。
  • 您能否添加您的data 的结构?更好的数据结构可以带来更快的更新。
  • @bennygenel 我已经更新了我的问题

标签: javascript android ios reactjs react-native


【解决方案1】:

我试过你的代码:

  • “我看到了一些奇怪的问题——尤其是在 iPhone 8 plus 上 模拟器(在 iPhone 6 模拟器或 Android 上看不到相同的 device)" ==> 我确认了这一点

  • 该应用占用大约 100% 的 CPU。

经过一段时间的尝试,我想通了:

  • “我看到了一些奇怪的问题——尤其是在 iPhone 8 plus 上 模拟器(在 iPhone 6 模拟器或 Android 上看不到相同的 device)" ==> 不对,稍等一下TextInput 会展开。

  • state 和 AsyncStorage 没有任何问题。我没有收到任何警告。

根本问题是FadeImage 中的动画:

  • 应用渲染了很多Carousel,每个Carousel都有AngleInvestorFadeImage。问题是FadeImage

  • FadeImage 运行 Animated,持续时间为 1000 => CPU 过载

    ==> 那为什么TextInput 添加滚动然后需要很长时间才能展开,而FlatList 看起来有问题,但没有。它们只是在缓慢更新。

解决方案:

  • 尝试评论FadeImage,您会看到问题消失了。

  • 不要同时启动这么多动画。如果出现就开始(例如:Carousel 中的第一张卡片)

更新

我遇到了您的问题:快速输入导致setState 调用了很多次。 对于这种情况,您可以使用 debounce

在 App.js 中

render() {
    console.log('render app.js')
    ...
                <Section
                  id={item.id}
                  title={item.title}
                  pitch={item.pitch}
                  updatePitch={debounce(this.updatePitch, 1000)} // the key is here
                  questions={item.questions}
                  ready={this.state.ready}
                />

您可以更改延迟并查看控制台日志以了解更多信息。正如我所尝试的,延迟大约 500 可以停止警告。

P/s:你应该尝试删除forceUpdate

【讨论】:

  • 非常感谢您花时间查看我的代码并试图帮助我 - 我真的很感激!我尝试将FadeImage 更改为普通的Image,但它的行为仍然很奇怪.. 但是你的权利似乎在一些大负载时窒息。
  • 我添加了一张奇怪行为的图片。我发现很难调试并且不知道在 SO 上需要什么帮助。
  • @Norfeldt 我更新了我的答案。也许可以解决你的问题。
  • 感谢您接受答案。我认为有很多卡片。因此,该应用程序对设备来说很重。有很多卡片,您可以尝试键入并等待。他们将正确布局。这需要时间。所以现在,问题是优化渲染对象。这是另一个问题。
  • 感谢您帮助我找出问题所在。我发现这是因为我将Carousels 放在了FlatList
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-17
  • 1970-01-01
  • 2021-06-15
  • 2019-08-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多