列表渲染

2020-05-12 17:46 更新

首先,讓我們看下在 Javascript 中如何轉化列表:

如下代碼,我們使用 map() 函數讓數組中的每一項翻倍,我們得到了一個新的數列 doubled 。

const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map((number) => number * 2)
console.log(doubled)

代碼打印出 [2, 4, 6, 8, 10]。

在 Taro 中,把數組轉化為數列元素的過程是相似的。

渲染多個組件

下面,我們使用 JavaScript 中的 map() 方法遍歷 numbers 數組。對數組中的每個元素返回 <Text> 標簽,最后我們得到一個數組 listItems:

const numbers = [...Array(100).keys()] // [0, 1, 2, ..., 98, 99]
const listItems = numbers.map((number) => {
  return <Text className='li'> 我是第 {number + 1} 個數字</Text>
})

這段代碼生成了一個 1 到 100 的數字列表。

Keys

但是在上面的代碼,你會得到一個報錯:提醒你當循環(huán)一個數組時應該提供 keys。Keys 可以在 DOM 中的某些元素被增加或刪除的時候幫助 Nerv/小程序 識別哪些元素發(fā)生了變化。因此你應當給數組中的每一個元素賦予一個確定的標識。

const numbers = [...Array(100).keys()] // [0, 1, 2, ..., 98, 99]
const listItems = numbers.map((number) => {
  return <Text
    key={String(number)}
    className='li'
    >
    我是第 {number + 1} 個數字
  </Text>
})

taroKeys

taroKey 適用于循環(huán)渲染原生小程序組件,賦予每個元素唯一確定標識,轉換為小程序的 wx:key。

const numbers = [...Array(100).keys()] // [0, 1, 2, ..., 98, 99]
const listItems = numbers.map((number) => {
  return (
    // native component
    <g-list
      taroKey={String(number)}
      className='g-list'
    >
    我是第 {number + 1} 個數字
    </g-list>
  )
})

元素的 key 在他的兄弟元素之間應該唯一

數組元素中使用的 key 在其兄弟之間應該是獨一無二的。然而,它們不需要是全局唯一的。當我們生成兩個不同的數組時,我們可以使用相同的 key:

class App extends Componenet {
  state = {
    posts: [
      {id: 1, title: 'Hello World', content: 'Welcome to learning Taro!'},
      {id: 2, title: 'Installation', content: 'You can install Taro from npm.'}
    ]
  }
  render () {
    const { posts } = this.state
    const sidebar = (
      <View>
        {posts.map((post) =>
          <Text key={post.id}>
            {post.title}
          </Text>
        )}
      </View>
    )
    const content = posts.map((post) => {
      return <View key={post.id}>
        <Text>{post.title}</Text>
        <Text>{post.content}</Text>
      </View>
    })
    return (
      <View>
        {sidebar}
        <View className="divider" />
        {content}
      </View>
    )
  }
}

key 會作為給 Taro 的提示,但不會傳遞給你的組件。如果您的組件中需要使用和 key 相同的值,請將其作為屬性傳遞:

const content = posts.map((post) => {
  return <View key={post.id} id={post.id} >
    <Text>{post.title}</Text>
    <Text>{post.content}</Text>
  </View>
})

key 的取值

key 的取值必須同時滿足三個條件:

  1. 穩(wěn)定
  2. 可預測
  3. 唯一(相對于其他兄弟元素)

最好的 key 就是數組里的 ID(通常由后端生成),他能同時滿足以上三個條件,同時也不需要自己去生成。如果沒有 ID,你能保證數組的元素某個鍵值字符串都是不同的(例如 item.title),那么使用那個字符串鍵值也可以。如果源數據沒有提供很好的 key 值,或者需要遍歷的數組生成的。那么你最好在數據創(chuàng)建或者修改之后給他添加一個好的 key 值:

let todoCounter = 0
function createNewTodo(text) {
  return {
    completed: false,
    id: todoCounter++,
    text
  }
}

class App extends Components {
  state = {
    todos: [],
    inputText: ''
  }

  onNewTodo () {
    this.setState({
      todos: [...this.state.todos, createNewTodo(this.state.inputText)]
    })
  }

  render () {
    return ...
  }
}

每一個在渲染結果上一致組件的應該對應一個相同的 key。因此使用數組的 index 或在數組渲染時隨機生成一個 key 值(但你在創(chuàng)建數組時可以這么做)都是反優(yōu)化,極端情況下甚至可能導致渲染出錯。

與 React 的不同

在 React 中,JSX 是會編譯成普通的 JS 的執(zhí)行,每一個 JSX 元素,其實會通過 createElement 函數創(chuàng)建成一個 JavaScript 對象(React Element),因此實際上你可以這樣寫代碼 React 也是完全能渲染的:

const list = this.state.list.map(l => {
  if (l.selected) {
    return <li>{l.text}</li>
  }
}).filter(React.isValidElement)

你甚至可以這樣寫:

const list = this.state.list.map(l => {
  if (l.selected) {
    return {
      '$$typeof': Symbol(react.element),
      'props': {
        children: l.text
      },
      'type': 'li'
    }
  }
}).filter(React.isValidElement)

但是 Taro 中,JSX 會編譯成微信小程序模板字符串,因此你不能把 map 函數生成的模板當做一個數組來處理。當你需要這么做時,應該先處理需要循環(huán)的數組,再用處理好的數組來調用 map 函數。例如上例應該寫成:

const list = this.state.list
  .filter(l => l.selected)
  .map(l => {
    return <li>{l.text}</li>
  })


以上內容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號