Vous avez probablement rencontré le même mur que de nombreux équipes React Native avec le NativeBase Picker. Le menu déroulant s'affiche, il ressemble bien, iOS fonctionne, et puis Android ignore votre onValueChange Pas de crash. Pas d'avertissement. Un sélecteur qui fonctionne comme prévu sans exécuter votre logique métier.
Cette lacune est la raison pour laquelle le NativeBase Picker surprend encore les développeurs expérimentés. La configuration est simple, la mise en forme est gérable, mais la fiabilité en production dépend de la compréhension d'une erreur Android spécifique que la plupart des guides ignorent ou ne mentionnent jamais.
Table des Matières
- Démarrage avec le NativeBase Picker
- Liaison d'état et gestion des sélections
- Dépannage du bug Android surValueChange
- Personnaliser votre composant de sélection
- Scénarios avancés et meilleures pratiques
- Conclusion
Démarrage avec le NativeBase Picker
Au sein d'un sprint, un sélecteur est souvent ajouté en fin de course. Sélecteur de pays, champ de statut, type de rendez-vous, option de livraison. Il semble petit jusqu'à ce que le comportement de la plateforme commence à se refléter dans votre interface utilisateur.
La bonne nouvelle est que le NativeBase Picker est facile à afficher sur l'écran. Il a été conçu pour afficher un sélecteur natif sur iOS et Android et pour remplacer le sélecteur React Native obsolète dans les anciens ensembles NativeBase, ce qui est la raison pour laquelle de nombreux anciens codebases y font encore appel.

Installez le composant dans un setup React Native normal
Si votre projet utilise déjà NativeBase, la principale tâche consiste à importer les primitives appropriées et à éviter la logique de wrapper inutile dès le premier jour. Commencez par le sélecteur de base le plus visible possible.
Si vous travaillez sur plusieurs stacks mobiles et hybrides, il est également utile de comprendre comment React code est emballé dans des environnements plus larges comme Flux de workflow d'application mobile React avec Capacitor. Le sélecteur code reste familier, mais les attentes de déploiement changent.
Un exemple de base ressemble à ceci:
import React, { useState } from 'react';
import { Container, Content, Form, Item, Picker, Icon } from 'native-base';
export default function BasicPickerScreen() {
const [selectedValue, setSelectedValue] = useState('key0');
return (
<Container>
<Content padder>
<Form>
<Item picker>
<Picker
mode="dropdown"
iosIcon={<Icon name="arrow-down" />}
selectedValue={selectedValue}
onValueChange={(value) => setSelectedValue(value)}
>
<Picker.Item label="Choose one" value="key0" />
<Picker.Item label="JavaScript" value="js" />
<Picker.Item label="TypeScript" value="ts" />
<Picker.Item label="React Native" value="rn" />
</Picker>
</Item>
</Form>
</Content>
</Container>
);
}
Affichez un premier sélecteur sans abstraction supplémentaire
Cette première passe devrait répondre qu'à deux questions :
- S'il est affiché correctement : Vous souhaitez vérifier que le champ apparait bien dans votre layout, respecte l'espace et s'ouvre sur les deux plateformes.
- Est votre chemin d'import correct : Les projets NativeBase échouent souvent pour des raisons banales comme la combinaison d'API de composants anciens et nouveaux.
- Est la valeur sélectionnée contrôlée : Même dans un prototype jetable, utilisez
selectedValuefrom state. Le sélecteur non contrôlé code devient plus difficile à déboguer ultérieurement.
Règle pratique : N'entamez pas par mapper les données du serveur, les règles de remplissage, les appels d'API et la validation dans le même sélecteur. Faites d'abord le composant visible et contrôlé.
Cela compte. Lorsque l'Android se comporte mal plus tard, vous saurez que le problème n'est pas votre architecture de formulaire entière. C'est le sélecteur.
Liaison d'état et gestion des sélections
Une fois le sélecteur affiché, la prochaine étape est de le rendre utile. Dans React Native, cela signifie traiter le sélecteur comme un champ de saisie contrôlé et conserver la valeur sélectionnée dans l'état du composant.
La voie standard est claire. Vous stockez la valeur actuelle avec useStateet la passez dans selectedValueet mettez à jour cet état à l'intérieur onValueChangeC'est le même état d'esprit que vous utiliseriez pour d'autres champs de formulaire tels que Modèles de champ de texte React NativeUtilisez une valeur contrôlée dès le début.
Utilisez une valeur contrôlée dès le début
Cela vous donne une source de vérité prévisible. L'interface utilisateur reflète
import React, { useState } from 'react';
import { Text } from 'react-native';
import { Form, Item, Picker } from 'native-base';
export default function RolePicker() {
const [role, setRole] = useState('');
return (
<>
<Form>
<Item picker>
<Picker
mode="dropdown"
selectedValue={role}
onValueChange={(value) => setRole(value)}
>
<Picker.Item label="Select a role" value="" />
<Picker.Item label="Admin" value="admin" />
<Picker.Item label="Editor" value="editor" />
<Picker.Item label="Viewer" value="viewer" />
</Picker>
</Item>
</Form>
<Text>Selected role: {role || 'none'}</Text>
</>
);
}
Cela code vous offre une source de vérité prévisible. L'interface utilisateur reflète roleConservez la logique de sélection petite et testable.
Gardez la logique de sélection simple et testable.
Problems usually start when developers overload onValueChange. Ils récupèrent des données, modifient plusieurs tranches d'état, déclenchent la navigation et enregistrent des analytics dans une fonction inline.
Quand le comportement du sélecteur faille, la débogage devient douloureux.
import React, { useEffect, useState } from 'react';
import { Text } from 'react-native';
import { Form, Item, Picker } from 'native-base';
export default function DepartmentPicker() {
const [department, setDepartment] = useState('');
const [message, setMessage] = useState('No department selected');
useEffect(() => {
if (!department) {
setMessage('No department selected');
return;
}
setMessage(`Department selected: ${department}`);
}, [department]);
return (
<>
<Form>
<Item picker>
<Picker
selectedValue={department}
onValueChange={setDepartment}
>
<Picker.Item label="Select department" value="" />
<Picker.Item label="Sales" value="sales" />
<Picker.Item label="Support" value="support" />
<Picker.Item label="Operations" value="operations" />
</Picker>
</Item>
</Form>
<Text>{message}</Text>
</>
);
}
Cette structure réalise deux choses utiles :
- Cela rend le sélecteur responsable uniquement de l'état de sélection en cours d'actualisation.
- Il déplace le comportement de l'application dans
useEffectElle déplace le comportement de l'application dans
Si un composant de formulaire ne peut pas déterminer avec certitude la valeur sélectionnée par votre application, tous les effets secondaires liés à celui-ci deviennent suspects.
Ce point devient critique sur Android, où le flux d'événements du NativeBase Picker n'est pas toujours respecté.
Résolution du Bug Android de la Modification de Valeur
This is the part most articles skip. The NativeBase Picker can look healthy on Android while failing at the exact moment you need it to do work.
Documentation communautaire autour de l'ancienne mise en œuvre du sélecteur décrit une véritable séparation multiplateforme. Android ne déclenche pas les fonctions personnalisées attachées à onValueChange, tandis que iOS le fait, et cette erreur est décrite comme une 100% de disparité fonctionnelle sur Android avec un taux de réussite de 0% pour le déclenchement des fonctions sur les appareils Android, malgré une mise en œuvre identique code Dans les rapports documentés. La même documentation indique aux développeurs des solutions de contournement ou de migration, et note que NativeBase 3.0 Select le composant atteint 98% de réussite des déclenchements de fonctions sur les deux plateformes dans les environnements testés en comparaison, comme décrit dans la documentation et le contexte de migration du NativeBase picker.

Ce qui brise réellement sur Android
La raison pratique est un détail d'implémentation. Sur Android, le NativeBase Picker repose sur le spinner natif, et ce niveau ne propage pas les écouteurs d'événements vers le wrapper comme le font de nombreux développeurs. Sur iOS, le comportement modal relie correctement au système d'événements.
C'est pourquoi ce bug ressemble à une supercherie. Vous voyez l'interface utilisateur. Vous pouvez ouvrir les options. Vous pouvez même sélectionner un élément visible. Mais votre fonction métier ne s'exécute jamais.
Un schéma de défaillance typique ressemble à ceci :
<Picker
selectedValue={status}
onValueChange={(value) => {
setStatus(value);
saveStatusToApi(value);
trackSelection(value);
updateDependentFields(value);
}}
>
Sur iOS, cela peut se comporter exactement comme prévu. Sur Android, le sélecteur peut s'afficher tout en ne déclenchant jamais ces fonctions.
Un contournement qui tient la route en production
Le contournement le plus fiable est architectural, et non cosmétique. Gardez l'interaction du sélecteur centrée sur la mise en mémoire d'une valeur sélectionnée, puis réagissez aux changements d'état en dehors du gestionnaire du sélecteur.
import React, { useEffect, useState } from 'react';
import { Form, Item, Picker } from 'native-base';
export default function StatusPicker() {
const [status, setStatus] = useState('');
const [didMount, setDidMount] = useState(false);
useEffect(() => {
if (!didMount) {
setDidMount(true);
return;
}
if (!status) return;
runStatusSideEffects(status);
}, [status, didMount]);
const runStatusSideEffects = (value) => {
console.log('Selected status:', value);
// call validation, API sync, or dependent form updates here
};
return (
<Form>
<Item picker>
<Picker
selectedValue={status}
onValueChange={setStatus}
>
<Picker.Item label="Select status" value="" />
<Picker.Item label="Pending" value="pending" />
<Picker.Item label="Approved" value="approved" />
<Picker.Item label="Rejected" value="rejected" />
</Picker>
</Item>
</Form>
);
}
Pourquoi cela aide :
- L'état reste central : Votre logique de composant surveille
statuspas seulement le payload de l'événement du sélecteur. - Les effets secondaires deviennent explicites : Les appels API, les mises à jour des champs dépendants et la suivi ne vivent plus dans un callback UI fragile.
- Le code est plus facile à remplacer ultérieurement : If vous migrez vers NativeBase Picker, la plupart de la logique commerciale survivent intactes.
Pour les projets Android, je recommande également de tester sur un appareil réel tôt, surtout si votre application a déjà une complexité de packaging natif. Configuration Android pour les applications Capacitor.
Lorsque la migration vers Select est la décision la plus propre.
Si le sélecteur se trouve dans un flux critique tel que le paiement, l'inscription ou l'entrée de données réglementées, il peut ne pas être utile de patcher autour de l'ancien comportement. À ce stade, passer à NativeBase 3.0 Select est généralement la meilleure option à long terme.
Le bug n'est pas juste un inconvénient. Il change où vous pouvez placer logique commerciale en toute sécurité.
Si vous maintenez l'ancien sélecteur, traitez-le comme un coquille de UI avec une responsabilité minimale. Cette mentalité prévient de nombreux régressions silencieux Android.
Stylage et thématisation de votre composant de sélection.
Un sélecteur fonctionnel ressemble encore à un projet inachevé s'il ne correspond pas au reste de l'application. NativeBase vous donne suffisamment de crochets pour rendre le contrôle délibéré, mais les résultats les plus propres proviennent généralement du style du conteneur autour du sélecteur plutôt que de lutter contre le contrôle natif directement.

Stylez le conteneur avant de styliser le sélecteur.
Le sélecteur est en partie contraint par la mise en page native. Le wrapper vous offre un contrôle plus grand sur l'espacement, le traitement des bordures et le rythme de la disposition.
Un modèle pratique :
import React, { useState } from 'react';
import { StyleSheet } from 'react-native';
import { Form, Item, Picker, Icon } from 'native-base';
export default function StyledPicker() {
const [country, setCountry] = useState('');
return (
<Form>
<Item style={styles.pickerWrapper} picker>
<Picker
mode="dropdown"
iosIcon={<Icon name="arrow-down" style={styles.icon} />}
textStyle={styles.pickerText}
selectedValue={country}
onValueChange={setCountry}
>
<Picker.Item label="Select country" value="" />
<Picker.Item label="Germany" value="de" />
<Picker.Item label="Japan" value="jp" />
<Picker.Item label="Brazil" value="br" />
</Picker>
</Item>
</Form>
);
}
const styles = StyleSheet.create({
pickerWrapper: {
borderWidth: 1,
borderColor: '#D1D5DB',
borderRadius: 10,
marginTop: 12,
paddingLeft: 8,
backgroundColor: '#FFFFFF',
},
pickerText: {
color: '#111827',
fontSize: 16,
},
icon: {
color: '#111827',
fontSize: 18,
},
});
Cette approche vous donne généralement ce dont vous avez besoin sans sur-engineérer les surcharges de thème.
Utilisez une présentation conforme au système d'exploitation
iOS et Android ont rarement besoin d'une mise en forme identique. Ils ont besoin d'une intention cohérente. Le même jeton visuel peut toujours nécessiter une mise en page différente, un placement d'icône ou un mode de sélection.
Des ajustements utiles incluent :
- Sur iOS : Donnez au champ de la respiration et prenez en compte la présentation modale par rapport aux étiquettes environnantes.
- Sur Android : Vérifiez la mise en page et la hauteur du spinner par défaut sur plusieurs appareils.
- Pour les deux : Maintenez le texte de remplissage visuellement distinct des options réelles.
Une comparaison courte aide :
| Préoccupation | iOS | Android |
|---|---|---|
| Comportement ouvert | Sentiment modal | Sentiment de spinner |
| Attentes d'icône | Souvent plus décoratif | Souvent plus fonctionnel |
| Problèmes de mise en page | L'agencement de remplissage peut paraître flou. | Le texte peut sembler serré |
If your UI includes gradients, layered cards, or high-contrast surfaces, align the picker wrapper with the same treatment you use elsewhere in the interface, much like the patterns used in Travail de gradient linéaire UI React Native.
Note de conception : Les utilisateurs jugent un sélecteur moins par le menu déroulant lui-même et plus par la façon dont le champ fermé s'insère dans le formulaire.
That’s why border radius, label spacing, and placeholder color matter more than exotic picker customization.
Scénarios Avancés et Meilleures Pratiques
Un cas d'edge récurrent est particulièrement ennuyeux sur iOS. Les développeurs ont souvent besoin de charger les valeurs du sélecteur à partir d'un serveur tout en empêchant l'entrée de la zone de masque d'être sélectionnable, mais les ressources officielles n'abordent pas directement cette voie.
One recurring edge case is especially annoying on iOS. Developers often need to load picker values from a server while preventing the placeholder entry from being selectable, yet official material often doesn’t address that path directly. Community discussion highlights that la valeur de l'emplacement peut rester sélectionnable sur iOS, ce qui entraîne un comportement incohérent dans les applications réelles, comme le note cette React Native community discussion about server-loaded picker values and placeholder selection.

Load picker options from server data safely
L’erreur que je vois le plus souvent est de traiter les données récupérées comme immédiatement prêtes pour le sélecteur. Gardez une couche de transformation légère entre votre réponse API et le composant.
import React, { useEffect, useState } from 'react';
import { Form, Item, Picker } from 'native-base';
export default function DynamicCategoryPicker() {
const [categories, setCategories] = useState([]);
const [selectedCategory, setSelectedCategory] = useState('');
useEffect(() => {
const loadCategories = async () => {
const response = await fetch('https://example.com/api/categories');
const data = await response.json();
const formatted = data.map((item) => ({
label: item.name,
value: String(item.id),
}));
setCategories(formatted);
};
loadCategories();
}, []);
return (
<Form>
<Item picker>
<Picker
selectedValue={selectedCategory}
onValueChange={setSelectedCategory}
>
<Picker.Item label="Select category" value="" />
{categories.map((item) => (
<Picker.Item
key={item.value}
label={item.label}
value={item.value}
/>
))}
</Picker>
</Item>
</Form>
);
}
Cet étape de mise en forme vous donne un contrat stable. Votre sélecteur n'a pas besoin de connaître la forme interne du API.
Empêcher la sélection du placeholder sur iOS
La règle la plus sûre est simple. N'abord pas le placeholder comme une option réelle, même si la bibliothèque de UI le rend facile à rendre.
Un modèle pratique :
- Utilisez une valeur de sentinelle vide : Considérez le placeholder comme
''ou une autre valeur d'application invalide. - Valider avant soumission : Reject the empty value in form validation, not just in the UI.
- Éteignez les actions en aval : N'activez le bouton de soumission qu'une valeur non de remplacement existe.
Pour des flux plus stricts, affichez le texte d'aide lorsque le remplacement reste sélectionné :
const isValidSelection = selectedCategory !== '';
Ensuite, bloquez vos boutons d'action ou vos appels API sur cette condition au lieu de vous fier à la présentation du sélecteur.
Accessibilité et habitudes de production
Les sélecteurs peuvent échapper aux revues d'accessibilité car ils ressemblent à des éléments natifs. Ils nécessitent toujours une étiquetage clair et un état prévisible.
Un peu d'habitude rapporte rapidement :
- Ajoutez des étiquettes d'accessibilité : Faites du champ compréhensible aux lecteurs d'écran.
- Conservez les étiquettes explicites : 'Pays' est mieux que 'Sélectionner'.
- Testez la restauration de l'état : Réouvrir une forme et confirmer que l'élément sélectionné précédemment apparaît correctement.
- Write unit tests around selection-driven logic: La logique en dehors de l'interface utilisateur compte le plus. testage unitaire du comportement React aident à protéger ces transitions d'état.
Un sélecteur est rarement critique en soi. Les changements d'état derrière lui sont généralement les plus importants.
C'est l'esprit qui maintient les formulaires dynamiques stables. Traitez le sélecteur NativeBase comme une surface d'entrée. Mettez les règles réelles dans votre flux d'état, de validation et de soumission.
Conclusion
Le sélecteur NativeBase est toujours utile lorsque vous comprenez où il se brise. La mise en place est simple, la personnalisation est faisable et les listes d'options dérivées du serveur sont gérables. Un piège critique est la gestion des événements Android. Si vous maintenez la logique métier en dehors des appels de rappel du sélecteur fragile et la déplacez dans les effets dérivés d'état, le composant devient beaucoup plus prévisible.
Pour les anciens codebases, ce workaround est souvent suffisant. Pour les flux critiques, la migration vers Select est généralement une meilleure option. Dans les deux cas, la clé est la même. N'ayez pas confiance dans le sélecteur juste parce qu'il s'affiche.
Si votre équipe développe des applications Capacitor ou Electron et souhaite envoyer des correctifs JavaScript, CSS, texte, configuration et ressources sans attendre la revue de l'App Store. Capgo is worth a look. It gives you signed live updates, rollout control, rollback protection, and release visibility so you can recover faster when UI bugs like picker regressions escape into production.