React Native Academy · Oppitunti

Datan välittäminen props-ominaisuuksilla

Määritelkää uudelleenkäytettävä komponentti, joka vastaanottaa props-ominaisuuksia, välittäkää arvot yläkomponentista ja dokumentoikaa odotetut prop-rakenteet PropTypesilla tai TypeScriptillä.

Oppitunti 1/413 vaihetta

Datan välittäminen props-ominaisuuksilla on ilmainen React Native Academy-oppitunti CoddyKitissä. Tämä on oppitunti 1/4. Voit lukea tästä oppimispolusta kokonaan mitkä tahansa 3 oppituntia ilmaiseksi — sen jälkeen CoddyKit PRO avaa kaikki oppitunnit sekä käytännön harjoittelun sisäänrakennetulla koodieditorilla ja ympäri vuorokauden toimivalla tekoälytuutorilla. Oppitunti kuuluu React Native Academy-oppimispolkuun, ja edistymisesi synkronoituu verkon ja CoddyKit-sovelluksen välillä. React Native Academy-kurssilla on yhteensä 4 oppituntia.

Mitä propsit ovat?

Propsit (lyhenne sanasta properties) ovat mekanismi, jolla React-komponentit vastaanottavat syötteitä yläkomponentiltaan. Yläkomponentti piirtää alikomponentin ja välittää sille tietoja JSX:n attribuuttien kautta – aivan kuten HTML-attribuutit, mutta arvona voi olla mikä tahansa JavaScript-arvo, kuten merkkijono, luku, olio, taulukko tai funktio. Alikomponentti vastaanottaa kaikki propsinsa yhtenä olioargumenttina. Propsit kulkevat yhteen suuntaan: yläkomponentilta alikomponentille. Alikomponentti ei voi koskaan muokata omia propsejaan, sillä ne ovat vain luettavissa.

import { Text, View } from 'react-native';

// Child component receives props
function Greeting({ name, age }) {
  return (
    <View>
      <Text>Hello, {name}!</Text>
      <Text>You are {age} years old.</Text>
    </View>
  );
}

// Parent passes data through props
export default function App() {
  return (
    <View style={{ flex: 1, padding: 24 }}>
      <Greeting name='Alice' age={30} />
      <Greeting name='Bob' age={25} />
    </View>
  );
}

Propsien destrukturointi

Voit käyttää propseja kahdella tavalla: yhtenä props-oliona (function Card(props) { props.title }) tai destrukturoimalla ne suoraan funktion määrittelyssä (function Card({ title, subtitle })). Destrukturointi on suositeltu tapa, koska se tekee hyväksytyistä propseista näkyviä ja koodista tiiviimpää. Voit myös antaa propseille oletusarvot destrukturointisyntaksilla: { size = 16, color = '#333' }. Tämä tarkoittaa, että prop on valinnainen ja saa oletusarvon, jos yläkomponentti ei välitä sitä.

import { Text, View, StyleSheet } from 'react-native';

// Destructuring with default values
function Badge({ label, color = '#4f86f7', size = 14 }) {
  return (
    <View style={[styles.badge, { backgroundColor: color }]}>
      <Text style={[styles.text, { fontSize: size }]}>{label}</Text>
    </View>
  );
}

export default function App() {
  return (
    <View style={{ gap: 8, padding: 16 }}>
      <Badge label='New' />                        {/* uses defaults */}
      <Badge label='Sale' color='#e74c3c' />       {/* custom color */}
      <Badge label='Pro' color='#2ecc71' size={12} />
    </View>
  );
}

const styles = StyleSheet.create({
  badge: { paddingHorizontal: 10, paddingVertical: 4, borderRadius: 12, alignSelf: 'flex-start' },
  text: { color: '#fff', fontWeight: 'bold' },
});

Erityyppisten propsien välittäminen

Propsit voivat sisältää minkä tahansa JavaScript-arvon. Merkkijonot eivät tarvitse aaltosulkeita (title='Hello'), mutta kaikki muut tyypit tarvitsevat – luvut (count={5}), totuusarvot (disabled={true} tai pelkkä disabled), oliot (style={{ color: 'red' }}), taulukot (items={[1,2,3]}) ja funktiot (onPress={handlePress}). Funktioiden välittäminen propseina on tapa, jolla alikomponentit viestivät takaisin yläkomponenteille: yläkomponentti määrittelee funktion ja välittää sen alaspäin, ja alikomponentti kutsuu sitä, kun jotakin tapahtuu. Tämä malli on keskeinen Reactin yksisuuntaisessa tietovirrassa.

import { View, Text, TouchableOpacity } from 'react-native';

// onPress is a function prop — callback from child to parent
function ConfirmButton({ label, count, disabled, onPress }) {
  return (
    <TouchableOpacity
      onPress={onPress}
      disabled={disabled}
      style={{ opacity: disabled ? 0.4 : 1, backgroundColor: '#4f86f7', padding: 12, borderRadius: 8 }}
    >
      <Text style={{ color: '#fff', fontWeight: 'bold' }}>
        {label} ({count})
      </Text>
    </TouchableOpacity>
  );
}

export default function App() {
  return (
    <View style={{ padding: 24 }}>
      <ConfirmButton
        label='Add to Cart'
        count={3}
        disabled={false}
        onPress={() => console.log('Added!')}
      />
    </View>
  );
}

children-prop

Erityinen children-prop sisältää kaiken, mitä sijoitat komponentin aloitus- ja lopetustagien väliin. Tämä mahdollistaa komposition – voit rakentaa kääre- tai säilökomponentteja, jotka soveltavat asettelua, tyylejä tai toimintaa kaikkeen niiden sisälle sijoitettuun sisältöön. React Nativen View-, ScrollView- ja TouchableOpacity-komponentit toimivat tällä tavalla. Määritä omia käärekomponentteja children-propin avulla luodaksesi uudelleenkäytettäviä asettelupohjia esimerkiksi korteille, modaaleille ja näyttöpohjille.

import { View, StyleSheet } from 'react-native';

// A Card wrapper that applies consistent styling
function Card({ children, style }) {
  return (
    <View style={[styles.card, style]}>
      {children}
    </View>
  );
}

export default function App() {
  return (
    <View style={{ padding: 16 }}>
      <Card>
        {/* Any content can go inside the Card */}
        <Text>Title</Text>
        <Text>Subtitle</Text>
        <Image source={{ uri: 'https://picsum.photos/200' }} style={{ width: 200, height: 100 }} />
      </Card>
    </View>
  );
}

const styles = StyleSheet.create({
  card: { backgroundColor: '#fff', borderRadius: 16, padding: 16, elevation: 3, marginBottom: 12 },
});

Propsien levittäminen spread-operaattorilla

Käytä JavaScriptin spread-operaattoria välittääksesi olion kaikki ominaisuudet yksittäisinä propseina. Tämä on kätevää, kun dataoliossa on useita kenttiä: <UserCard {...user} /> vastaa koodia <UserCard id={user.id} name={user.name} avatar={user.avatar} />. Spread-malli on yleinen, kun taulukon alkioita piirretään .map()-menetelmällä. Älä levitä tuntemattomia propseja natiivikomponenteille (View, Text), sillä se aiheuttaa varoituksia tai ne ohitetaan hiljaisesti. Levitä propseja vain hallitsemillesi mukautetuille komponenteille.

import { View, Text } from 'react-native';

const users = [
  { id: '1', name: 'Alice', role: 'Admin' },
  { id: '2', name: 'Bob', role: 'User' },
];

function UserRow({ name, role }) {
  return (
    <View style={{ padding: 12, borderBottomWidth: 1, borderBottomColor: '#eee' }}>
      <Text style={{ fontWeight: 'bold' }}>{name}</Text>
      <Text style={{ color: '#666' }}>{role}</Text>
    </View>
  );
}

export default function UserList() {
  return (
    <View>
      {users.map(user => (
        <UserRow key={user.id} {...user} />
      ))}
    </View>
  );
}

TypeScript-propsit interface-määrittelyillä

Määritä TypeScriptissä propsien rakenne interface- tai type-määrittelyllä. Tämä havaitsee virheet käännösaikana: jos välität merkkijonon sijaan luvun tai unohdat pakollisen propin, saat virheilmoituksen jo ennen sovelluksen suorittamista. Merkitse valinnaiset propsit ?-merkillä. TypeScript mahdollistaa myös editorin automaattisen täydennyksen: kun kirjoitat JSX:ssä <MyComponent, editori näyttää kaikki käytettävissä olevat propsit ja niiden tyypit. Tämä on yksi TypeScriptin suurimmista tuottavuushyödyistä React Native -projekteissa.

import { View, Text, StyleSheet } from 'react-native';

interface ProductCardProps {
  id: string;
  name: string;
  price: number;
  imageUri: string;
  badge?: string;      // optional
  onPress?: () => void; // optional callback
}

export default function ProductCard({
  name,
  price,
  imageUri,
  badge,
  onPress,
}: ProductCardProps) {
  return (
    <View style={styles.card}>
      <Text style={styles.name}>{name}</Text>
      <Text style={styles.price}>${price.toFixed(2)}</Text>
      {badge && <Text style={styles.badge}>{badge}</Text>}
    </View>
  );
}

const styles = StyleSheet.create({
  card: { padding: 16, backgroundColor: '#fff', borderRadius: 12 },
  name: { fontSize: 16, fontWeight: '600' },
  price: { fontSize: 18, color: '#4f86f7', fontWeight: 'bold', marginTop: 4 },
  badge: { color: '#e74c3c', fontSize: 12, marginTop: 4 },
});

Takaisinkutsupropsit: viestintä alikomponentilta yläkomponentille

Koska data kulkee Reactissa vain alaspäin, alikomponentti viestii takaisin yläkomponentille kutsumalla propina välitettyä takaisinkutsufunktiota. Yläkomponentti määrittelee funktion (mitä käyttäjän toiminnasta pitäisi seurata), välittää sen alikomponentille, ja alikomponentti kutsuu sitä oikealla hetkellä. Tämä on painikkeiden, lomakesyötteiden ja luettelon alkioiden vakiomalli – alikomponentin ei tarvitse tietää, mitä seuraavaksi tapahtuu (se ei sisällä liiketoimintalogiikkaa), vaan se kutsuu prop-funktiota ja välittää sille tarvittavat tiedot.

import { View, Text, TouchableOpacity } from 'react-native';

// Child: knows nothing about business logic
function DeleteButton({ itemId, onDelete }) {
  return (
    <TouchableOpacity onPress={() => onDelete(itemId)}>
      <Text style={{ color: 'red' }}>Delete</Text>
    </TouchableOpacity>
  );
}

// Parent: owns the logic and state
export default function ItemList() {
  const [items, setItems] = React.useState(['Apple', 'Banana', 'Cherry']);

  function handleDelete(index) {
    setItems(prev => prev.filter((_, i) => i !== index));
  }

  return (
    <View>
      {items.map((item, i) => (
        <View key={i} style={{ flexDirection: 'row', justifyContent: 'space-between', padding: 12 }}>
          <Text>{item}</Text>
          <DeleteButton itemId={i} onDelete={handleDelete} />
        </View>
      ))}
    </View>
  );
}

Propsien porautumisen välttäminen

Propsien porautumista tapahtuu, kun välität propseja useiden sellaisten välikomponenttien kautta, jotka eivät itse tarvitse tietoja vaan ainoastaan välittävät ne syvemmälle. Esimerkiksi käyttäjäolion välittäminen ketjussa App → Screen → Section → Card → Avatar vain avatar-kuvan näyttämistä varten. Tätä on vaikea ylläpitää hierarkian kasvaessa. Ratkaisu on käyttää Contextia (yleisille tai jaetuille tiedoille, kuten kirjautuneelle käyttäjälle tai teemalle) tai sijoittaa tila samaan paikkaan niitä tarvitsevien komponenttien lähelle. Älä rakenna liian monimutkaista ratkaisua liian aikaisin – muutama propsien välitystaso on täysin hyväksyttävä.

// Prop drilling anti-pattern (not ideal when deep)
function App() {
  const user = { name: 'Alice', avatar: 'https://...' };
  return <Screen user={user} />;
}
function Screen({ user }) {
  return <Section user={user} />; // just passing through
}
function Section({ user }) {
  return <Avatar user={user} />; // just passing through
}
function Avatar({ user }) {
  return <Image source={{ uri: user.avatar }} style={{ width: 40, height: 40 }} />; // actually uses it
}

// Solution: Context (covered later)
// Or: pass only the specific value needed at each level
function Section({ avatarUri }) { ... }
function Avatar({ uri }) { ... }

Muuttumattomat propsit: miksi alikomponentit eivät voi muokata niitä

Reactin keskeinen sääntö on, että komponentti ei saa koskaan muokata omia propsejaan. Propsit ovat yläkomponentin alikomponentille näyttämistä varten välittämiä tietoja – yläkomponentti omistaa ne. Jos alikomponentin on muutettava arvoa, sen tulee kutsua callback-propia ilmoittaakseen siitä yläkomponentille, joka päivittää tilaansa. Tämän jälkeen yläkomponentti välittää päivitetyn arvon takaisin uutena propina. Tämä yksisuuntainen virtaus tekee tietojen muutoksista ennakoitavia ja helposti jäljitettäviä – tiedät aina, missä tila sijaitsee ja kuka hallitsee sitä.

// WRONG: Never mutate props directly
function Counter({ count }) {
  // This is an error — do not do this:
  // count = count + 1;

  return <Text>{count}</Text>;
}

// CORRECT: Notify parent via callback, parent updates state
function Counter({ count, onIncrement }) {
  return (
    <TouchableOpacity onPress={onIncrement}>
      <Text>{count}</Text>
    </TouchableOpacity>
  );
}

// In the parent:
const [count, setCount] = useState(0);
<Counter count={count} onIncrement={() => setCount(c => c + 1)} />

Propsien käytännön esimerkki: syötejulkaisu

Sovella kaikkia propsikäsitteitä realistiseen sosiaalisen median syötejulkaisukomponenttiin. Komponentti vastaanottaa syöteluettelolta tietoja (teksti, kuva, kirjoittaja, tykkäysten määrä ja aikaleima) sekä takaisinkutsufunktioita käyttäjän toimintoja varten (tykkääminen, kommentointi ja jakaminen). Komponentti vastaa vain esityksestä – kaikki data tulee propseina ja kaikki toiminnot välittyvät ulospäin callback-propsien kautta. Tämän rakenteen ansiosta FeedPost-komponentti on helppo testata erillään ja käyttää uudelleen erityyppisissä syötteissä (etusyöte, profiilisyöte ja hakutulokset).

import { View, Text, Image, TouchableOpacity, StyleSheet } from 'react-native';

export default function FeedPost({
  author, avatarUri, imageUri, caption, likes, timeAgo,
  onLike, onComment, onShare
}) {
  return (
    <View style={styles.post}>
      <View style={styles.header}>
        <Image source={{ uri: avatarUri }} style={styles.avatar} />
        <View>
          <Text style={styles.author}>{author}</Text>
          <Text style={styles.time}>{timeAgo}</Text>
        </View>
      </View>
      <Image source={{ uri: imageUri }} style={styles.postImage} />
      <Text style={styles.caption}>{caption}</Text>
      <View style={styles.actions}>
        <TouchableOpacity onPress={onLike}><Text>❤️ {likes}</Text></TouchableOpacity>
        <TouchableOpacity onPress={onComment}><Text>💬 Comment</Text></TouchableOpacity>
        <TouchableOpacity onPress={onShare}><Text>🔗 Share</Text></TouchableOpacity>
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  post: { backgroundColor: '#fff', marginBottom: 8 },
  header: { flexDirection: 'row', alignItems: 'center', padding: 12, gap: 10 },
  avatar: { width: 40, height: 40, borderRadius: 20 },
  author: { fontWeight: 'bold', fontSize: 14 },
  time: { fontSize: 12, color: '#999' },
  postImage: { width: '100%', aspectRatio: 1 },
  caption: { padding: 12, fontSize: 14 },
  actions: { flexDirection: 'row', gap: 20, padding: 12, borderTopWidth: 1, borderTopColor: '#eee' },
});

Oletuspropsit defaultPropsilla

Destrukturoinnin oletusarvojen lisäksi React-komponentit voivat määrittää staattisena ominaisuutena defaultProps-olion, joka sisältää kunkin valinnaisen propin oletusarvot. Kun propin arvo on undefined (yläkomponentti ei välittänyt sitä), React käyttää defaultProps-olion arvoa. Tämä malli on vanhempi kuin destrukturoinnin oletusarvot ja toimii samalla tavalla, mutta se on harvinaisempi nykyaikaisissa TypeScript-koodikannoissa, joissa oletusarvot määritetään mieluummin interfacen yhteydessä. Molemmilla tavoilla saavutetaan sama tulos: valinnaisia propseja voidaan käyttää turvallisesti ilman null-tarkistuksia.

import { Text, View, StyleSheet } from 'react-native';

function Avatar({ size, color, initials }) {
  return (
    <View style={[styles.circle, { width: size, height: size, borderRadius: size / 2, backgroundColor: color }]}>
      <Text style={styles.initials}>{initials}</Text>
    </View>
  );
}

// defaultProps: applied when prop is undefined
Avatar.defaultProps = {
  size: 48,
  color: '#4f86f7',
};

// Usage:
<Avatar initials='AB' />                    // uses defaults: 48px blue
<Avatar initials='CD' size={64} />          // custom size, default color
<Avatar initials='EF' size={32} color='#e74c3c' />  // all custom

const styles = StyleSheet.create({
  circle: { alignItems: 'center', justifyContent: 'center' },
  initials: { color: '#fff', fontWeight: 'bold', fontSize: 16 },
});

Pikatarkistus

Testaa, miten hyvin ymmärrät tämän oppitunnin React Native Mobile Development -käsitteet.

Oppitunnin yhteenveto

Tässä oppitunnissa opit, että propsit ovat vain luettavia arvoja, jotka välitetään yhteen suuntaan yläkomponentilta alikomponentille, callback-funktiopropsit mahdollistavat sen, että alikomponentit ilmoittavat yläkomponenteille käyttäjän toiminnoista, ja TypeScript-interfacet dokumentoivat sekä varmistavat odotetun propsirakenteen käännösaikana. Seuraavaksi tutustumme komponenttien sisällä muuttuvien arvojen hallintaan useState-hookilla.

Aloita maksutta

Opi JavaScript tekoälytuutorin avulla — ilmaiseksi

Kirjoita ja suorita oikeaa koodia selaimessa, saa välitöntä apua tekoälytuutorilta ympäri vuorokauden ja jatka siitä, mihin jäit, verkossa tai sovelluksessa.

Kurssit
30
Oppitunnit
120

Usein kysytyt kysymykset

Onko oppitunti ”Datan välittäminen props-ominaisuuksilla” ilmainen?

Kyllä — voit lukea täällä verkossa kokonaan ilmaiseksi mitkä tahansa React Native Academy-oppimispolun 3 oppituntia, myös oppitunnin “Datan välittäminen props-ominaisuuksilla”. Sen jälkeen CoddyKit PRO avaa kaikki oppitunnit sekä interaktiiviset harjoitukset sisäänrakennetulla koodieditorilla ja ympäri vuorokauden toimivalla tekoälytuutorilla. React Native Academy-kurssilla on yhteensä 4 oppituntia.

Mitä opin oppitunnilla ”Datan välittäminen props-ominaisuuksilla”?

Määritelkää uudelleenkäytettävä komponentti, joka vastaanottaa props-ominaisuuksia, välittäkää arvot yläkomponentista ja dokumentoikaa odotetut prop-rakenteet PropTypesilla tai TypeScriptillä. Harjoittelet React Native Academy-aihetta koodilla, jonka suoritat suoraan selaimessa. Ympäri vuorokauden käytettävissä oleva tekoälytuutori vastaa kysymyksiisi oppitunnin aikana.

Tarvitsenko kokemusta aloittaakseni React Native Academy-opiskelun?

Aiempi kokemus ei ole tarpeen. CoddyKitin React Native Academy-oppimispolku sopii vasta-alkajista edistyneisiin, joten voit aloittaa tästä tai alusta ja edetä omaan tahtiisi. Tämä on oppitunti 1/4.

Kuinka kauan ”Datan välittäminen props-ominaisuuksilla”-oppitunnin suorittaminen kestää?

Useimmat CoddyKitin oppitunnit kestävät noin 5–10 minuuttia. Jokainen oppitunti on lyhyt ja interaktiivinen, joten edistyt tasaisesti ja voit jatkaa siitä, mihin jäit – sekä verkossa että sovelluksessa.

Voinko kirjoittaa ja suorittaa koodia tällä React Native Academy-oppitunnilla?

Kyllä. Jokainen React Native Academy-oppitunti sisältää sisäänrakennetun koodieditorin, joten voit kirjoittaa ja suorittaa oikeaa koodia suoraan selaimessa ja saada välitöntä palautetta tekoälyltä – paikallista asennusta ei tarvita.

Kaikki tämän kurssin oppitunnit

  1. Datan välittäminen props-ominaisuuksilla
  2. Komponentin tilan hallinta useStatella
  3. Tilan nostaminen ylemmälle tasolle
  4. Vuorovaikutteisen laskurisovelluksen rakentaminen
← Takaisin: React Native Academy