Løfte tilstanden opp
Flytt delt tilstand til den nærmeste felles overordnede komponenten, og send både verdien og en callback for oppdatering som props for å holde søskenkomponentene synkronisert.
Løfte tilstanden opp er en gratis leksjon i React Native Academy på CoddyKit. Dette er leksjon 3 av 4. Du kan lese valgfritt 3 leksjoner fra denne læringsstien gratis i sin helhet – deretter låser CoddyKit PRO opp alle leksjoner, samt praktisk øving med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Den er en del av læringsløpet i React Native Academy, og fremdriften din synkroniseres mellom nettet og CoddyKit-appen. Kurset i React Native Academy inneholder totalt 4 leksjoner.
Problemet: Søskenkomponenter trenger delt tilstand
Se for Dem to søskenkomponenter som trenger å dele en dataverdi — for eksempel en komponent med et søkefelt og en resultatliste som filtreres basert på søket. Hvis søketilstanden ligger i søkefeltkomponenten, har resultatlisten ingen måte å få tilgang til den på. Ingen av søskenkomponentene kan se den andres tilstand direkte. Løsningen er å løfte tilstanden opp til den nærmeste felles forfederen — den overordnede komponenten som gjengir begge søskenkomponentene. Forelderen sender deretter tilstandsverdien til den ene underkomponenten og setter-tilbakekallet til den andre.
// PROBLEM: SearchInput holds state that ResultsList needs
// but siblings can't communicate directly
function SearchInput() {
const [query, setQuery] = useState(''); // stuck here
return <TextInput value={query} onChangeText={setQuery} />;
}
function ResultsList() {
// How do we access 'query' from the sibling? We can't!
return <FlatList data={/* needs query */} />;
}Løfte tilstanden til den felles forfaderen
Flytt den delte tilstanden til den nærmeste felles forfaderen — komponenten som gjengir begge søskenkomponentene. Forelderen eier nå tilstanden og sender den videre som props: søket sendes til resultatlisten, og setter-funksjonen sendes til søkefeltet. Søskenkomponentene kommuniserer gjennom forelderen: Søkefeltet kaller setter-funksjonen (sendt som en prop), som oppdaterer forelderens tilstand. Den nye søkeverdien sendes deretter videre til resultatlisten som en ny prop. Dette er det grunnleggende dataflytmønsteret i React.
import { useState } from 'react';
import { View, TextInput, Text } from 'react-native';
// Parent owns the shared state
export default function SearchScreen() {
const [query, setQuery] = useState('');
const results = ITEMS.filter(item =>
item.toLowerCase().includes(query.toLowerCase())
);
return (
<View style={{ flex: 1, padding: 16 }}>
<SearchInput query={query} onQueryChange={setQuery} />
<ResultsList results={results} />
</View>
);
}
const ITEMS = ['Apple', 'Banana', 'Blueberry', 'Cherry', 'Avocado'];Skrive underkomponentene
Med løftet tilstand blir underkomponentene enkle og fokuserte. Komponenten SearchInput mottar query (gjeldende verdi) og onQueryChange (for å oppdatere den) som props. ResultsList mottar arrayen med filtrerte resultater. Ingen av underkomponentene håndterer tilstand — de er kontrollerte komponenter hvis atferd styres fullstendig av props. Det gjør begge komponentene enkle å teste isolert: send inn props, og kontroller det gjengitte resultatet.
import { TextInput, FlatList, Text, View, StyleSheet } from 'react-native';
// Controlled: receives value and updater as props
function SearchInput({ query, onQueryChange }) {
return (
<TextInput
value={query}
onChangeText={onQueryChange}
placeholder='Search...'
style={styles.input}
/>
);
}
// Pure display: receives filtered data as prop
function ResultsList({ results }) {
return (
<FlatList
data={results}
keyExtractor={item => item}
renderItem={({ item }) => (
<Text style={styles.item}>{item}</Text>
)}
/>
);
}
const styles = StyleSheet.create({
input: { borderWidth: 1, borderColor: '#ccc', borderRadius: 8, padding: 12, marginBottom: 12 },
item: { padding: 12, borderBottomWidth: 1, borderBottomColor: '#eee' },
});Finne den nærmeste felles forfaderen
Det viktige spørsmålet når De løfter tilstand, er: «Hvilken komponent i treet er den nederste som gjengir alle komponentene som trenger denne tilstanden?» Løft tilstanden til akkurat denne komponenten — ikke høyere. Hvis De løfter den for høyt (for eksempel helt til App), fører det til unødvendige nye gjengivelser av komponenter som ikke har noen forbindelse med tilstanden. Den nærmeste felles forfaderen gir den rette balansen mellom tilgjengelighet (alle forbrukere kan motta tilstanden som props) og ytelse (bare undertreet som trenger tilstanden, gjengis på nytt). Tegn komponenttreet, og følg hver forbruker oppover for å finne hvor banene møtes.
// Component tree:
// App
// └── TabBar (selected tab)
// ├── HomeTab
// │ └── Feed
// └── ProfileTab
//
// If 'selected tab' is needed by TabBar AND by Feed to filter content,
// lift it to TabBar (nearest common ancestor of both).
// DO NOT lift all the way to App unnecessarily.
function TabBar() {
const [activeTab, setActiveTab] = useState('home');
return (
<View>
<TabButtons activeTab={activeTab} onChange={setActiveTab} />
{activeTab === 'home' ? <HomeTab /> : <ProfileTab />}
</View>
);
}Flere søskenkomponenter som deler tilstand
Når tre eller flere søskenkomponenter trenger den samme tilstanden, løfter De den én gang til den felles forelderen og sender den til hver søskenkomponent som trenger den. En produktskjerm kan for eksempel ha en ImageGallery, en VariantPicker og en AddToCartButton. Tilstanden for den valgte varianten ligger i den overordnede skjermkomponenten og sendes til alle tre. VariantPicker får setter-funksjonen for å endre valget. ImageGallery viser bilder for den valgte varianten. AddToCartButton legger den valgte varianten i handlekurven.
import { useState } from 'react';
import { View } from 'react-native';
const VARIANTS = ['Red / S', 'Red / M', 'Blue / S', 'Blue / M'];
export default function ProductDetailScreen() {
const [selectedVariant, setSelectedVariant] = useState(VARIANTS[0]);
return (
<View style={{ flex: 1 }}>
<ImageGallery variant={selectedVariant} />
<VariantPicker
variants={VARIANTS}
selected={selectedVariant}
onSelect={setSelectedVariant}
/>
<AddToCartButton variant={selectedVariant} />
</View>
);
}Tilbakeringings-props kontra delt tilstand
Noen ganger deler komponenter en handling i stedet for en tilstandsverdi. Et vanlig eksempel er en listeskjerm med et handlekurvikon i toppteksten — når De trykker på «Legg i handlekurv» for et listeelement, skal antallet i merket i toppteksten oppdateres. Både listeelementene og toppteksten er søskenkomponenter under den samme overordnede skjermen. Forelderen har antallet i handlekurven i tilstanden, sender antallet til toppteksten via props og sender et onAddToCart-tilbakekall til listen. Elementene kaller tilbakekallet, forelderen oppdaterer tilstanden, og merket i toppteksten gjengis på nytt med det nye antallet.
import { useState } from 'react';
import { View } from 'react-native';
export default function ShopScreen() {
const [cartCount, setCartCount] = useState(0);
function addToCart(item) {
// Business logic lives in the parent
setCartCount(prev => prev + 1);
console.log('Added:', item.name);
}
return (
<View style={{ flex: 1 }}>
{/* Sibling 1: displays count from parent state */}
<ShopHeader cartCount={cartCount} />
{/* Sibling 2: triggers parent state update */}
<ProductList onAddToCart={addToCart} />
</View>
);
}Når løftet tilstand blir prop-drilling
Hvis den felles forfaderen ligger mange nivåer over komponentene som trenger tilstanden, ender De opp med prop-drilling — props sendes gjennom mange mellomliggende komponenter som ikke bruker dem. Hvis den felles forfaderen for eksempel er App-roten, og det er fem komponentnivåer mellom App og forbrukerne, blir hver mellomliggende komponent en budbringer. Løsningen for deling på dypet er Context API (for data som mange komponenter trenger) eller et bibliotek for tilstandshåndtering som Zustand eller Redux. Tommelfingerregelen er: Løft tilstanden, men hvis den går gjennom mer enn 2–3 nivåer uten å bli brukt underveis, bør De vurdere Context i stedet.
// When prop drilling gets too deep, switch to Context
// Instead of:
function App() {
const [user, setUser] = useState(null);
return <Nav user={user}> // not used
<Drawer user={user}> // not used
<Screen user={user}> // not used
<ProfileCard user={user} /> // finally used
</Screen>
</Drawer>
</Nav>;
}
// Use Context:
const UserContext = React.createContext(null);
function App() {
const [user, setUser] = useState(null);
return (
<UserContext.Provider value={user}>
<Nav><Drawer><Screen><ProfileCard /></Screen></Drawer></Nav>
</UserContext.Provider>
);
}Praktisk eksempel: Temperaturkonvertering
En klassisk demonstrasjon av å løfte tilstand er to temperaturinndatafelt (Celsius og Fahrenheit) som holdes synkronisert. Hvert inndatafelt mottar den gjeldende temperaturen og et onChange-tilbakekall. Forelderen lagrer temperaturen i én enhet (for eksempel Celsius) og konverterer den til det andre inndatafeltet. Når ett av feltene endres, konverterer forelderen verdien og oppdaterer tilstanden. Begge feltene mottar den riktige verdien fra forelderens tilstand, slik at de holdes perfekt synkronisert gjennom den delte tilstanden hos forelderen.
import { useState } from 'react';
import { View, Text, TextInput, StyleSheet } from 'react-native';
function TempInput({ label, value, onChangeText }) {
return (
<View style={styles.row}>
<Text style={styles.label}>{label}</Text>
<TextInput
value={value}
onChangeText={onChangeText}
keyboardType='decimal-pad'
style={styles.input}
/>
</View>
);
}
export default function TemperatureConverter() {
const [celsius, setCelsius] = useState('');
const fahrenheit = celsius !== '' ? (parseFloat(celsius) * 9/5 + 32).toFixed(1) : '';
function handleFahrenheitChange(f) {
const c = f !== '' ? ((parseFloat(f) - 32) * 5/9).toFixed(1) : '';
setCelsius(c);
}
return (
<View style={{ padding: 24 }}>
<TempInput label='Celsius' value={celsius} onChangeText={setCelsius} />
<TempInput label='Fahrenheit' value={fahrenheit} onChangeText={handleFahrenheitChange} />
</View>
);
}
const styles = StyleSheet.create({
row: { flexDirection: 'row', alignItems: 'center', marginBottom: 12 },
label: { width: 100, fontSize: 16 },
input: { flex: 1, borderWidth: 1, borderColor: '#ccc', borderRadius: 8, padding: 10 },
});Plasser tilstanden nær bruksstedet
Det motsatte prinsippet av å løfte tilstanden er å holde tilstanden så lavt som mulig. Hvis bare én komponent trenger en tilstandsverdi, skal De beholde den i denne komponenten — ikke løfte den unødvendig. En nedtrekksmenys åpne/lukkede-tilstand trenger for eksempel ikke å ligge i den overordnede skjermen med mindre forelderen må reagere på den. Når lokal tilstand forblir lokal, forbedres ytelsen (færre komponenter gjengis på nytt), og koden blir enklere å forstå (tilstanden og brukergrensesnittet som bruker den, er på samme sted). Løft bare tilstanden når to eller flere komponenter faktisk trenger de samme dataene.
// Local state: Accordion open/close is private to Accordion
function Accordion({ title, children }) {
const [open, setOpen] = useState(false); // stays local — parent doesn't care
return (
<View>
<TouchableOpacity onPress={() => setOpen(prev => !prev)}>
<Text>{title} {open ? '▲' : '▼'}</Text>
</TouchableOpacity>
{open && <View style={{ paddingLeft: 16 }}>{children}</View>}
</View>
);
}
// Only lift open state to parent IF the parent needs to:
// - Know which accordion is open to close others
// - Save/restore the open state on navigation
// Otherwise, keep it local!Ukontrollerte kontra kontrollerte komponenter
En komponent er kontrollert når tilstanden styres fullstendig av props (og endres via tilbakekall) — slik som i eksempelet med søkefeltet. Den er ukontrollert når den håndterer sin egen interne tilstand uten å gjøre den tilgjengelig for forelderen. I React bruker De som oftest kontrollerte komponenter fordi de er forutsigbare og enkle å teste. Ukontrollerte inndatafelt (der ref brukes til å lese verdier) brukes av og til av hensyn til ytelsen (for å unngå nye gjengivelser ved hvert tastetrykk) eller for integrasjon med kode som ikke er skrevet i React. Standardinndatafeltene i React Native, som TextInput, er kontrollerte når De sender inn value og onChangeText.
import { TextInput, useRef } from 'react-native';
// Uncontrolled: parent reads value on demand via ref
function UncontrolledInput({ inputRef }) {
return (
<TextInput
ref={inputRef}
// No value prop: TextInput manages its own text
defaultValue='Initial text'
/>
);
}
// Parent reads the value when the form is submitted:
function Form() {
const inputRef = useRef(null);
function handleSubmit() {
// Use _lastNativeText (not recommended) or store value in ref.current
console.log('Value:', inputRef.current?.props?.value);
}
}Praktisk eksempel: Filter- og resultatpanel
En praktisk anvendelse av løftet state er en søkeside med en filterlinje (kategorivelger, glidebryter for prisintervall og sorteringsvelger) over en resultatliste. Hver filterverdi ligger i den overordnede SearchScreen-komponenten. Filterlinjen mottar gjeldende filterverdier og tilbakeringingsfunksjoner for oppdatering som props. Resultatlisten mottar de gjeldende filtrene og gjengis på nytt hver gang et filter endres. Den overordnede komponenten henter også nye resultater hver gang filtrene endres (via useEffect med filteravhengigheter). Denne koordineringen gjennom én overordnet komponent gjør søkefunksjonen forutsigbar og enkel å teste.
import { useState, useEffect } from 'react';
import { View } from 'react-native';
export default function SearchScreen() {
// All filter state lifted here
const [query, setQuery] = useState('');
const [category, setCategory] = useState('all');
const [sortBy, setSortBy] = useState('relevance');
const [results, setResults] = useState([]);
// Fetch results whenever any filter changes
useEffect(() => {
fetchResults({ query, category, sortBy }).then(setResults);
}, [query, category, sortBy]);
return (
<View style={{ flex: 1 }}>
<FilterBar
query={query} onQueryChange={setQuery}
category={category} onCategoryChange={setCategory}
sortBy={sortBy} onSortChange={setSortBy}
/>
<ResultsList results={results} />
</View>
);
}Hurtigsjekk
Test forståelsen Deres av React Native Mobile Development-konseptene fra denne leksjonen.
Oppsummering av leksjonen
I denne leksjonen lærte De at løftet state til nærmeste felles overordnede komponent gjør at søskenkomponenter kan dele data gjennom den overordnede komponenten, at kontrollerte komponenter mottar verdien sin og oppdateringsfunksjonen som props, noe som gjør dem forutsigbare og enkle å teste, og at state bør ligge så langt nede i komponenttreet som mulig – løft den bare når flere komponenter faktisk trenger den. Deretter bygger vi en interaktiv tellerapp som kombinerer alle disse konseptene.
Lær deg JavaScript med en AI-veileder – gratis
Skriv og kjør ekte kode i nettleseren, få umiddelbar hjelp fra en AI-veileder som er tilgjengelig døgnet rundt, og fortsett der du slapp – på nettet eller i appen.
- Kurs
- 30
- Leksjoner
- 120
Ofte stilte spørsmål
Er leksjonen «Løfte tilstanden opp» gratis?
Ja – du kan lese valgfritt 3 av leksjonene i læringsstien React Native Academy, inkludert «Løfte tilstanden opp», gratis i sin helhet her på nettet. Deretter låser CoddyKit PRO opp alle leksjoner, samt interaktiv øving med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Kurset i React Native Academy inneholder totalt 4 leksjoner.
Hva lærer jeg i «Løfte tilstanden opp»?
Flytt delt tilstand til den nærmeste felles overordnede komponenten, og send både verdien og en callback for oppdatering som props for å holde søskenkomponentene synkronisert. Du øver på React Native Academy med praktisk kode som du kjører direkte i nettleseren, mens en AI-veileder som er tilgjengelig døgnet rundt, svarer på spørsmålene dine mens du jobber deg gjennom leksjonen.
Trenger jeg erfaring for å begynne med React Native Academy?
Ingen tidligere erfaring er nødvendig. React Native Academy på CoddyKit er lagt opp for både nybegynnere og viderekomne, så De kan begynne her eller helt fra start og lære i Deres eget tempo. Dette er leksjon 3 av 4.
Hvor lang tid tar leksjonen «Løfte tilstanden opp»?
De fleste CoddyKit-leksjoner tar omtrent 5–10 minutter. Hver leksjon er kort og interaktiv, slik at De gjør jevne fremskritt og kan fortsette akkurat der De slapp – både på nettet og i appen.
Kan jeg skrive og kjøre kode i denne React Native Academy-leksjonen?
Ja. Alle React Native Academy-leksjoner har en innebygd kodeeditor, slik at De kan skrive og kjøre ekte kode direkte i nettleseren og få umiddelbar tilbakemelding fra AI – uten lokal konfigurering.
Alle leksjonene i dette kurset
- Sende data med props
- Håndtere komponenttilstand med useState
- Løfte tilstanden opp
- Bygge en interaktiv tellerapp