Komponentin tilan hallinta useStatella
Tutustukaa useState-hookiin, tallentakaa laskuri- tai vaihtokytkinarvo tilaan ja käynnistäkää uudelleenrenderöinnit kutsumalla tilan asetinfunktion.
Komponentin tilan hallinta useStatella on ilmainen React Native Academy-oppitunti CoddyKitissä. Tämä on oppitunti 2/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ä tila on?
Tila on ajan mittaan muuttuvaa dataa, jonka komponentti omistaa ja jota se hallitsee sisäisesti. Toisin kuin propsit, jotka tulevat yläkomponentilta ja ovat vain luettavissa, tila luodaan ja päivitetään komponentin itsensä toimesta. Kun tila muuttuu, React piirtää komponentin uudelleen automaattisesti, jotta uudet arvot näkyvät käyttöliittymässä. Yleisiä tilaesimerkkejä ovat modaalin avoimuus, käyttäjän kirjoittama teksti, valittuna oleva välilehti tai API:sta haetut alkiot. Tila tekee React-komponenteista vuorovaikutteisia.
import { View, Text, TouchableOpacity } from 'react-native';
import { useState } from 'react';
export default function Counter() {
// Declare state: count starts at 0
const [count, setCount] = useState(0);
return (
<View style={{ padding: 24, alignItems: 'center' }}>
<Text style={{ fontSize: 48, fontWeight: 'bold' }}>{count}</Text>
<TouchableOpacity
onPress={() => setCount(count + 1)}
style={{ marginTop: 16, padding: 12, backgroundColor: '#4f86f7', borderRadius: 8 }}
>
<Text style={{ color: '#fff', fontSize: 18 }}>Increment</Text>
</TouchableOpacity>
</View>
);
}useState-hookin allekirjoitus
useState on React Hook, jota kutsutaan komponenttifunktion ylimmällä tasolla. Se saa yhden argumentin – tilan alkuarvon – ja palauttaa taulukon, jossa on kaksi alkiota: nykyinen tila-arvo ja asetusfunktio. Nämä puretaan taulukon destrukturoinnilla. Nimeämiskäytäntö on [value, setValue]. Asetusfunktiota kutsutaan tilan päivittämiseen – tilamuuttujaa ei saa koskaan muuttaa suoraan. Kukin tilamuuttuja on itsenäinen, joten kutsu useState-hookia useita kertoja, jos tarvitset useita erillisiä tilan osia.
import { useState } from 'react';
function MyComponent() {
// Syntax: const [value, setter] = useState(initialValue);
const [count, setCount] = useState(0); // number
const [name, setName] = useState(''); // string
const [isVisible, setIsVisible] = useState(false); // boolean
const [items, setItems] = useState([]); // array
const [user, setUser] = useState(null); // object or null
// Update state by calling the setter:
// setCount(5); ← sets to 5
// setName('Alice'); ← sets to 'Alice'
// setIsVisible(true); ← sets to true
}Tilapäivitykset käynnistävät uudelleenpiirron
Kun kutsut tilan asetusfunktiota, React ajastaa komponentin uudelleenpiirron. Seuraavan piirron aikana useState palauttaa uuden arvon. React käsittelee tämän tehokkaasti: se niputtaa saman tapahtumankäsittelijän aikana tapahtuvat useat tilapäivitykset ja tekee vain yhden uudelleenpiirron. On myös tärkeää muistaa, että nykyisen piirron tila-arvo on muuttumaton. Kutsu setCount(count + 1) ei muuta count-arvoa välittömästi nykyisen funktion suorituksen aikana – uusi arvo näkyy seuraavalla piirrolla.
import { useState } from 'react';
import { View, Text, TouchableOpacity } from 'react-native';
export default function Example() {
const [count, setCount] = useState(0);
function handlePress() {
// These do NOT stack: count is still 0 in this render
setCount(count + 1); // schedules count = 1
setCount(count + 1); // also schedules count = 1, not 2!
// To correctly base on previous value, use functional update:
// setCount(prev => prev + 1); // prev = 0, result = 1
// setCount(prev => prev + 1); // prev = 1, result = 2
}
return (
<TouchableOpacity onPress={handlePress}>
<Text>{count}</Text>
</TouchableOpacity>
);
}Funktiomuotoiset päivitykset edellisen tila-arvon avulla
Kun uusi tila riippuu edellisestä tilasta, käyttäkää aina funktionaalista päivitysmuotoa: setState(prev => prev + 1). React takaa, että prev sisältää uusimman tila-arvon, vaikka useita päivityksiä käsiteltäisiin yhdessä. Tämä on olennaista, kun laskureita kasvatetaan, totuusarvoja vaihdetaan tai taulukoihin lisätään kohteita nopeasti peräkkäin (esimerkiksi painiketta nopeasti napautettaessa). Objektiliteraalimuoto (setState(state + 1)) voi aiheuttaa virheitä, jos useita päivityksiä tapahtuu ennen komponentin uudelleenrenderöintiä.
import { useState } from 'react';
import { View, Text, TouchableOpacity } from 'react-native';
export default function SafeCounter() {
const [count, setCount] = useState(0);
function increment() {
// Safe: uses previous state, handles batching correctly
setCount(prev => prev + 1);
}
function decrement() {
setCount(prev => Math.max(0, prev - 1)); // min 0
}
function reset() {
setCount(0); // direct value ok when not based on previous
}
return (
<View style={{ alignItems: 'center', gap: 12, padding: 24 }}>
<Text style={{ fontSize: 48 }}>{count}</Text>
<TouchableOpacity onPress={increment}><Text>+</Text></TouchableOpacity>
<TouchableOpacity onPress={decrement}><Text>-</Text></TouchableOpacity>
<TouchableOpacity onPress={reset}><Text>Reset</Text></TouchableOpacity>
</View>
);
}Totuusarvotilan vaihtaminen
Hyvin yleinen toimintamalli on vaihtaa totuusarvotilaa true- ja false-arvojen välillä. Käyttäkää funktionaalista päivitysmuotoa loogisen NOT-operaattorin kanssa: setIsOpen(prev => !prev). Tämä on selkeämpi kuin setIsOpen(!isOpen), koska se käsittelee nopeasti peräkkäiset painallukset oikein. Yleisiä käyttötapauksia ovat modaalin näyttäminen ja piilottaminen, haitariosion avaaminen ja sulkeminen, toiston ja tauon välillä vaihtaminen sekä suosikkitähtikuvakkeen vaihtaminen. Totuusarvot ovat ehkä yleisimmin käytetty komponenttitilan tyyppi.
import { useState } from 'react';
import { View, Text, TouchableOpacity, Modal, StyleSheet } from 'react-native';
export default function ToggleModal() {
const [isVisible, setIsVisible] = useState(false);
return (
<View style={styles.container}>
<TouchableOpacity
style={styles.btn}
onPress={() => setIsVisible(prev => !prev)}
>
<Text style={styles.btnText}>Toggle Modal</Text>
</TouchableOpacity>
<Modal visible={isVisible} transparent animationType='fade'>
<View style={styles.overlay}>
<Text style={styles.modalText}>Hello from the modal!</Text>
<TouchableOpacity onPress={() => setIsVisible(false)}>
<Text style={{ color: '#fff', marginTop: 16 }}>Close</Text>
</TouchableOpacity>
</View>
</Modal>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, justifyContent: 'center', alignItems: 'center' },
btn: { padding: 12, backgroundColor: '#4f86f7', borderRadius: 8 },
btnText: { color: '#fff', fontWeight: 'bold' },
overlay: { flex: 1, backgroundColor: 'rgba(0,0,0,0.7)', justifyContent: 'center', alignItems: 'center' },
modalText: { color: '#fff', fontSize: 20, fontWeight: 'bold' },
});Merkkijonotila: hallittu tekstikenttä
TextInput-komponentin sitominen tilaan luo hallittavan syötekentän. value-prop määrittää näytettävän tekstin tilasta, ja onChangeText-takaisinkutsu päivittää tilan aina, kun käyttäjä kirjoittaa. Tällöin tila on syötekentän arvon ainoa totuuden lähde — voitte lukea, validoida tai muuntaa tekstin milloin tahansa lukemalla tila-arvon. Ilman tilaan sitomista (eli hallitsemattomassa syötekentässä) tarvitsisitte ref-viittauksen syötekentän arvon lukemiseen, mikä ei ole Reactissa yhtä tavanomaista.
import { useState } from 'react';
import { View, Text, TextInput, StyleSheet } from 'react-native';
export default function SearchBox() {
const [query, setQuery] = useState('');
return (
<View style={styles.container}>
<TextInput
value={query}
onChangeText={setQuery} // same as: (text) => setQuery(text)
placeholder='Search...'
style={styles.input}
/>
<Text style={styles.preview}>
You typed: <Text style={{ fontWeight: 'bold' }}>{query}</Text>
</Text>
<Text style={styles.count}>{query.length} characters</Text>
</View>
);
}
const styles = StyleSheet.create({
container: { padding: 16, gap: 8 },
input: { borderWidth: 1, borderColor: '#ccc', borderRadius: 8, padding: 12, fontSize: 16 },
preview: { fontSize: 14, color: '#555' },
count: { fontSize: 12, color: '#999' },
});Taulukkotila: kohteiden lisääminen ja poistaminen
Kun tila on taulukko, älkää koskaan muuttako sitä suoraan push- tai splice-metodilla. Luokaa sen sijaan uusi taulukko, joka sisältää muutoksen, ja välittäkää se tilan päivitysfunktiolle. Kohteen lisääminen: setItems(prev => [...prev, newItem]). Kohteen poistaminen: setItems(prev => prev.filter(item => item.id !== id)). Kohteen päivittäminen: setItems(prev => prev.map(item => item.id === id ? {...item, done: true} : item)). Näiden muuttumattomuutta säilyttävien toimintamallien ansiosta React tunnistaa muutokset ja ajoittaa tehokkaat uudelleenrenderöinnit.
import { useState } from 'react';
import { View, Text, TouchableOpacity, TextInput, FlatList } from 'react-native';
export default function TodoList() {
const [todos, setTodos] = useState([]);
const [input, setInput] = useState('');
function addTodo() {
if (!input.trim()) return;
setTodos(prev => [...prev, { id: Date.now().toString(), text: input.trim(), done: false }]);
setInput('');
}
function toggleDone(id) {
setTodos(prev => prev.map(t => t.id === id ? { ...t, done: !t.done } : t));
}
return (
<View style={{ flex: 1, padding: 16 }}>
<TextInput value={input} onChangeText={setInput} placeholder='Add a todo' />
<TouchableOpacity onPress={addTodo}><Text>Add</Text></TouchableOpacity>
<FlatList
data={todos}
keyExtractor={t => t.id}
renderItem={({ item }) => (
<TouchableOpacity onPress={() => toggleDone(item.id)}>
<Text style={{ textDecorationLine: item.done ? 'line-through' : 'none' }}>
{item.text}
</Text>
</TouchableOpacity>
)}
/>
</View>
);
}Objektitila
Kun tilan osat liittyvät toisiinsa, ryhmitelkää ne yhdeksi objektimuuttujaksi. Objektitilaa päivittäessänne levittäkää edellinen tila, jotta muuttumattomat kentät säilyvät: setState(prev => ({ ...prev, name: 'Alice' })). Tämä korvaa vain name-kentän ja säilyttää kaikki muut kentät ennallaan. Vaihtoehtoisesti jokainen kenttä voi olla oma useState-tilansa — kumpikaan tapa ei ole yleisesti toista parempi. Käyttäkää objektia, kun kentät muuttuvat aina yhdessä (kuten lomakkeessa, jossa on nimi, sähköpostiosoite ja salasana), ja erillisiä tiloja, kun kentät muuttuvat toisistaan riippumatta.
import { useState } from 'react';
import { View, TextInput, Text, StyleSheet } from 'react-native';
export default function ProfileForm() {
const [form, setForm] = useState({
name: '',
email: '',
bio: '',
});
function updateField(field, value) {
setForm(prev => ({ ...prev, [field]: value }));
}
return (
<View style={{ padding: 16, gap: 12 }}>
<TextInput
value={form.name}
onChangeText={v => updateField('name', v)}
placeholder='Name'
style={styles.input}
/>
<TextInput
value={form.email}
onChangeText={v => updateField('email', v)}
placeholder='Email'
style={styles.input}
/>
<Text style={{ color: '#666' }}>Preview: {form.name} — {form.email}</Text>
</View>
);
}
const styles = StyleSheet.create({
input: { borderWidth: 1, borderColor: '#ccc', borderRadius: 8, padding: 12 },
});Laiska alustustila
Jos alkutilan laskeminen on kallista (esimerkiksi tietojen lukeminen AsyncStoragesta tai suuren tietojoukon jäsentäminen), välittäkää useState-funktiolle arvon sijaan funktio: useState(() => computeExpensiveValue()). React kutsuu tätä alustajafunktiota vain kerran komponentin ensimmäisen renderöinnin yhteydessä, ei jokaisen myöhemmän renderöinnin aikana. Tätä kutsutaan laiskaksi alustukseksi. Kalliin laskutoimituksen välittäminen tavallisena arvona (useState(expensiveCompute())) suorittaisi sen jokaisella renderöinnillä ja aiheuttaisi tarpeetonta työtä, vaikka alkutilaa käytetään vain kerran.
import { useState } from 'react';
// Without lazy init: computeExpensiveList() runs on EVERY render
const [list, setList] = useState(computeExpensiveList()); // BAD
// With lazy init: function is called ONCE on mount
const [list, setList] = useState(() => computeExpensiveList()); // GOOD
// Real example: JSON.parse is cheap, but still good practice:
const [settings, setSettings] = useState(() => {
const stored = SomeStorage.get('settings');
return stored ? JSON.parse(stored) : { theme: 'light', lang: 'en' };
});Johdetut arvot ja tarpeeton tila
Älkää tallentako tilaan arvoja, jotka voidaan johtaa olemassa olevasta tilasta tai propseista. Jos tilassa on esimerkiksi tehtävälista, älkää tallentako myös completedCount-arvoa tilaan — laskekaa se renderöinnin aikana: todos.filter(t => t.done).length. Johdettujen arvojen tallentaminen tilaan aiheuttaa synkronointivirheitä: saatatte unohtaa päivittää toisen arvon päivittäessänne toista. Sijoittakaa tilaan vain sellaiset asiat, joita ei voi johtaa olemassa olevista arvoista, laskea renderöinnin aikana tai jotka asetetaan asynkronisesti (kuten API-vastaustiedot). Tämä sääntö pitää tilan suppeana ja virheettömänä.
import { useState } from 'react';
import { Text, View } from 'react-native';
export default function TodoStats() {
const [todos, setTodos] = useState([
{ id: '1', text: 'Buy groceries', done: true },
{ id: '2', text: 'Write code', done: false },
{ id: '3', text: 'Go for a walk', done: true },
]);
// DERIVED: computed from existing state, not stored in state
const total = todos.length;
const completed = todos.filter(t => t.done).length;
const remaining = total - completed;
return (
<View style={{ padding: 16 }}>
<Text>Total: {total}</Text>
<Text>Completed: {completed}</Text>
<Text>Remaining: {remaining}</Text>
</View>
);
}useState ja useRef: milloin tilaa ei pidä käyttää
Kaikkien komponentissa muuttuvien arvojen ei tarvitse olla tilassa. Jos arvo muuttuu mutta sen ei tarvitse käynnistää uudelleenrenderöintiä, tallentakaa se sen sijaan useRef-viittaukseen. Yleisiä esimerkkejä ovat setTimeout-funktion palauttama ajastimen tunniste, verkkopyynnön käynnissäoloa seuraava lippu tai propin edellinen arvo. Näiden arvojen sijoittaminen tilaan aiheuttaa tarpeettomia uudelleenrenderöintejä. useRef säilyttää arvonsa renderöintien välillä tilan tavoin, mutta sen päivittäminen ei ajoita uutta renderöintiä. Nyrkkisääntö: jos käyttöliittymän ei tarvitse muuttua arvon muuttuessa, käyttäkää useRef-viittausta.
import { useState, useRef, useEffect } from 'react';
import { Text, TouchableOpacity, View } from 'react-native';
export default function Timer() {
const [elapsed, setElapsed] = useState(0);
const intervalRef = useRef(null); // timer ID in ref — no re-render needed
function start() {
if (intervalRef.current) return; // already running
intervalRef.current = setInterval(() => {
setElapsed(prev => prev + 1); // state — needs re-render to show
}, 1000);
}
function stop() {
clearInterval(intervalRef.current);
intervalRef.current = null;
}
useEffect(() => () => clearInterval(intervalRef.current), []); // cleanup
return (
<View style={{ alignItems: 'center', padding: 24, gap: 16 }}>
<Text style={{ fontSize: 48 }}>{elapsed}s</Text>
<TouchableOpacity onPress={start}><Text>Start</Text></TouchableOpacity>
<TouchableOpacity onPress={stop}><Text>Stop</Text></TouchableOpacity>
</View>
);
}Pikatarkistus
Testatkaa, miten hyvin ymmärrätte tämän oppitunnin React Native Mobile Development -käsitteet.
Oppitunnin yhteenveto
Tässä oppitunnissa opitte, että useState määrittää tilamuuttujia ja tarjoaa uudelleenrenderöinnin käynnistävän päivitysfunktion, funktionaalisilla päivityksillä uusi tila voidaan johtaa turvallisesti edellisestä arvosta ja johdetut arvot tulee laskea renderöinnin aikana sen sijaan, että ne tallennettaisiin tarpeettomana tilana. Seuraavaksi tutustumme tilan nostamiseen ylemmälle tasolle tietojen jakamiseksi sisaruskomponenttien välillä.
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 ”Komponentin tilan hallinta useStatella” ilmainen?
Kyllä — voit lukea täällä verkossa kokonaan ilmaiseksi mitkä tahansa React Native Academy-oppimispolun 3 oppituntia, myös oppitunnin “Komponentin tilan hallinta useStatella”. 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 ”Komponentin tilan hallinta useStatella”?
Tutustukaa useState-hookiin, tallentakaa laskuri- tai vaihtokytkinarvo tilaan ja käynnistäkää uudelleenrenderöinnit kutsumalla tilan asetinfunktion. 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 2/4.
Kuinka kauan ”Komponentin tilan hallinta useStatella”-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
- Datan välittäminen props-ominaisuuksilla
- Komponentin tilan hallinta useStatella
- Tilan nostaminen ylemmälle tasolle
- Vuorovaikutteisen laskurisovelluksen rakentaminen