Как вы все знаете, свойства, которые кратко называются props, являются одним из фундаментальных блоков React. Реквизит позволит вам передавать параметры между компонентами, это могут быть строки, числа, объекты или массивы.

Вот простой пример использования реквизита.

import React from 'react'
import {View, Text} from 'react-native'
export function Greetings(props){
return (
<Text>Hello {props.name}</Text>
)
}

export default function Home() {
return (
<Greetings name='meVasanth Medium blog readers' />
)
}

Компонент Home использует компонент Greetings в качестве дочернего компонента, который принимает имя в качестве реквизита. Если вы выполните этот код, вывод будет

Hello meVasanth Medium blog readers

Как работает реактивный реквизит?

Многие из вас, возможно, не задавали этот вопрос, пока учились реагировать. Это станет очень важным, когда вы начнете работать над сложным проектом и столкнетесь с трудностями в понимании некоторых вопросов, связанных с реквизитом (один из них упомянут в названии статьи).

Позвольте мне объяснить, как работает реактивный реквизит. Простыми словами реквизиты ведут себя так же, как аргументы функции. В приведенном выше примере мы можем считать, что Greetings — это функция, которая принимает имя в качестве аргумента и отображает его везде, где вы его используете.

Это объяснение очень важно, потому что все работает нормально в случае примитивных типов данных (целые, строковые и логические). Но когда вы передаете ссылочные типы в качестве свойств, вы начинаете сталкиваться с несколькими нежелательными сценариями.

Рассмотрим пример ниже; Home.js имеет два реквизита Child1 и Child2, и оба они используют пользовательский массив в качестве реквизита. Child1 изменяет массив и отображает элементы, тогда как child2 просто отображает элементы.

Home.js

import React from 'react'
import {View} from 'react-native'
import Child1 from './Child1';
import Child2 from './Child2';
export default function Home() {
const user = [
{name: 'User 1'},
{name: 'User 2'},
{name: 'User 3'}
]
return (
<View>
<Child1 userDetails={user}/>
<Child2 userDetails={user}/>
</View>
)
}

child1.js

import React from 'react'
import { View, Text } from 'react-native';
export default function Child1({
userDetails = [{}]
}) {
userDetails.forEach(item => {
item.name =  ' Test Name' + item.name
})
return (
<View>
<Text>Child 1</Text>
{
userDetails.map(item => {
return (
<View>
<Text>{item.name}</Text>
</View>
)
})
}
</View>
)
}

child2.js

import React from 'react'
import { View,Text } from 'react-native';
export default function Child2({
userDetails = [{}]
}) {
return (
<View>
<Text>Child 2</Text>
{
userDetails.map(item => {
return (
<View>
<Text>{item.name}</Text>
</View>
)
})
}
</View>
)
}

Попробуйте угадать вывод?

Идеальное ожидание состоит в том, что Child1 должен отображать все имена с добавленным к нему Test Name, а Child2 должен отображать все имена, как они есть в массиве.

Но происходит так, что и Child1, и Child2 будут отображать имена, к которым добавляется тестовое имя.

Поскольку теперь вы знаете, как на самом деле работают реквизиты внутри React, вы можете догадаться, почему это происходит. Поскольку мы передаем одну и ту же ссылку на массив обоим реквизитам, изменение значений в одном компоненте влияет на другой.

Как решить эту проблему

Решение 1

В приведенном выше фрагменте мы допустили ошибку, изменив свойства, отправленные компоненту. Одно из невысказанных правил использования реквизита — не изменять его внутри компонента. Если вам нужно внести какие-то изменения в реквизит, то сделайте его копию внутри компонента.

Например:

Создайте переменную состояния внутри Child1 и измените эту переменную состояния.

import React, { useEffect, useState } from 'react'
import { View, Text } from 'react-native';
export default function Child1({
userDetails = [{}]
}) {
const [userInfo, setUserInfo] = useState([])
useEffect(() => {
const temp = []
for (let i = 0; i < userDetails.length; i++) {
temp.push({
name: 'Test Name' + userDetails[i].name
})
}
setUserInfo(temp)
}, [])
return (
<View>
<Text>Child 1</Text>
{
userInfo.map(item => {
return (
 <View>
    <Text>{item.name}</Text>
 </View>
)
})
}
</View>
)
}

Если список содержит меньше данных, этот подход работает нормально. Но проблема возникает, когда список очень большой. В этом случае это увеличит память, используемую во время выполнения.

Решение 2

Глубокое копирование. Вместо отправки одной и той же ссылки на оба компонента мы можем отправить уникальную ссылку на каждый компонент. Это решит проблему. Вы можете создать глубокую копию либо с помощью

  1. Библиотека Loadash — я рекомендую этот подход, потому что loadash — популярная библиотека, их способ глубокого копирования менее требователен к процессору и используется во многих популярных приложениях.
  2. JSON.parse(JSON.stringify(user)) — отлично работает, когда список содержит меньше элементов и не содержит вызовов функций. Если содержит вызов функции, этот подход не работает.

Есть несколько других подходов, которым также можно следовать, чтобы избежать этой проблемы. Если вы знаете о таком подходе, пожалуйста, напишите об этом в комментариях.

Приятного чтения..

Другие статьи того же автора:

  1. Как все обстоит с Object в JavaScript?
  2. Сделать запрос Get с заголовком для рендеринга изображения в React Native
  3. Является ли JavaScript Array.push() глубокой или поверхностной копией?
  4. Проблема с возвратом значений из асинхронных функций ожидания

Все статьи автора читайте здесь.