Vous avez probablement rencontré le même mur que de nombreux équipes de React Native avec le NativeBase Picker. Le menu déroulant s'affiche, il a l'air correct, iOS fonctionne, et puis Android ignore votre onValueChange logique. Pas de crash. Pas de warning. Juste un sélecteur qui semble fonctionnel tandis que votre logique métier ne s'exécute jamais.
C'est ce fossé qui fait que le NativeBase Picker surprend encore les développeurs expérimentés. La mise en place est simple, la mise en forme est gérable, mais la fiabilité en production dépend de la compréhension d'une erreur spécifique à Android que la plupart des guides ignorent ou n'évoquent jamais.
Table des matières
- Démarrage avec le NativeBase Picker
- Liage d'état et gestion des sélections
- Dépannage du bug Android onValueChange
- Stylage et Thématique de Votre Composant de Sélection
- Scénarios Avancés et Meilleures Pratiques
- Conclusion
Démarrage avec le Sélecteur NativeBase
Un sélecteur est généralement l'un de ces composants que vous ajoutez tard dans un sprint. Sélecteur de pays, champ de statut, type de rendez-vous, option de livraison. Il a l'air petit jusqu'à ce que le comportement de la plateforme commence à se refléter dans votre interface.
La bonne nouvelle est que le Sélecteur NativeBase est facile à afficher à 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 codebases legacy dépendent encore de lui.

Installez le composant dans un environnement React Native normal
Si votre projet utilise déjà NativeBase, la principale tâche est d'importer les primitives appropriées et d'éviter la logique de wrapper inutile dès le départ. Commencez par le sélecteur de date le plus simple 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 Les workflows d'applications mobiles React avec CapacitorLe 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 seulement à deux questions:
- Est-ce qu'il s'affiche correctement: Vous voulez vérifier que le champ apparait dans votre disposition, respecte les espaces et s'ouvre sur les deux plateformes.
- Est-ce que votre chemin d'importation est correct: Les projets NativeBase échouent souvent pour des raisons banales telles que la combinaison d'API de composants anciens et nouveaux.
- Est-ce que la valeur sélectionnée est contrôlée: Même dans un prototype jetable, utilisez
selectedValueà partir de l'état. Le sélecteur non contrôlé code devient plus difficile à déboguer ultérieurement.
Règle pratique : N'oubliez pas de ne pas commencer par la mise en correspondance des données de serveur, des règles de remplissage, des hooks d'analytique et de la validation dans le même sélecteur. Faites d'abord le composant visible et contrôlé.
Cette base minimale compte. Lorsque Android commence à se comporter de manière anormale ultérieurement, vous saurez que le problème ne réside pas dans l'architecture de votre formulaire entier. C'est le sélecteur.
Liaison de l'état et gestion des sélections
Une fois le sélecteur affiché, le prochain travail consiste à 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 simple. Vous stockez la valeur actuelle avec useStateet la passez dans selectedValueet mettez à jour cet état à l'intérieur onValueChangeCe modèle est le même que celui utilisé pour d'autres champs de formulaire comme les modèles de React Native TextInputmême si le contrôle de l'interface utilisateur est différent
Utilisez une valeur contrôlée dès le début
Voici la version propre que j'utiliserais avant d'ajouter des validations ou des effets secondaires :
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 donne une source de vérité prévisible. L'interface utilisateur reflète roleet chaque fonction downstream lit dans le même état.
Gardez la logique de sélection petite et testable
Les problèmes commencent généralement lorsque les développeurs surchargent onValueChangeIls récupèrent des données, mutent plusieurs tranches d'état, déclenchent la navigation et enregistrent des analytics en une seule fonction inline. Lorsque le comportement du sélecteur faille, le débogage devient douloureux.
Un modèle plus approprié est de séparer la mise en cache de la valeur des effets secondaires :
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 fait deux choses utiles :
- Elle rend le sélecteur responsable uniquement de l'état de sélection.
- Elle déplace le comportement de l'application dans
useEffectoù vous pouvez tester et raisonner à son sujet de manière independante.
Si un composant de formulaire ne peut pas vous dire avec certitude quel est la valeur sélectionnée, tous les effets secondaires attachés à lui deviennent suspects.
Ce point devient critique sur Android, où le flux d'événements de Picker NativeBase prévu ne tient pas toujours.
Détecter le Bug de l'événement onValueChange sur Android
C'est là où les articles passent généralement sous silence. Le Picker NativeBase peut avoir l'air en bonne santé sur Android tout en échouant exactement au moment où vous avez besoin qu'il fasse du travail.
La documentation communautaire autour de l'ancienne mise en œuvre du sélecteur décrit une véritable scission cross-platform. Android ne déclenche pas les fonctions personnalisées attachées à onValueChange, tandis que iOS le fait, et ce dysfonctionnement est décrit comme un 100% de déséquilibre fonctionnel 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. Les mêmes documents orientent les développeurs vers des workarounds ou des migrations, et notent que le NativeBase 3.0 Select atteint 98% de taux de déclenchement de fonction 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 casse réellement sur Android
La raison pratique est détaillée dans l'implémentation. Sur Android, le NativeBase Picker repose sur le spinner natif, et ce niveau n'propage pas les écouteurs d'événements vers le wrapper comme le font de nombreux développeurs. Sur iOS, le comportement basé sur le modèle se lie correctement au système d'événements.
C'est pourquoi ce bug ressemble à un piège. Vous voyez l'interface utilisateur. Vous pouvez ouvrir les options. Vous pouvez même sélectionner un élément visible. Mais votre fonction commerciale ne s'exécute jamais.
Un modèle 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 que les fonctions ne s'exécutent jamais.
Un travail de 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 le stockage d'une valeur sélectionnée, puis réagissez aux changements d'état en dehors du gestionnaire de 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-t-il ?
- L'état reste central : Votre logique de composant surveille
status, et non le payload de l'événement de sélecteur seul. - Les effets secondaires deviennent explicites : API appels, mises à jour de champs dépendants et suivi ne vivent plus à l'intérieur d'un callback UI fragile.
- Le code est plus facile à remplacer ultérieurement : Si vous migrez vers NativeBase Picker, la plupart de la logique commerciale survit inchangée.
Dans les projets axés sur Android, je recommande également de tester sur un appareil réel tôt, surtout si votre application a déjà une complexité de packaging native comme Configuration Android pour les applications Capacitor.
When la migration vers Select est la décision la plus propre
Si le sélecteur est intégré dans un flux critique comme la facturation, l'inscription ou l'entrée de données réglementées, les patchs autour du comportement ancien ne valent peut-être pas la peine. À ce stade, passer à NativeBase 3.0 Select est généralement la décision à long terme la plus sûre.
Le bug n'est pas juste un inconvénient. Il change l'emplacement où vous pouvez placer logique métier de manière sûre.
Si vous maintenez le sélecteur ancien, traitez-le comme une coquille de l'interface utilisateur avec une responsabilité minimale. Cette mentalité empêche de nombreux regressions silencieuses Android.
Stylage et thématisation de votre composant de sélection
Un sélecteur fonctionnel ressemble encore à un produit 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 stylage du conteneur autour du sélecteur plutôt que de lutter directement contre le contrôle natif.

Stylisez le conteneur avant de styliser le sélecteur
Le sélecteur lui-même est en partie contraint par la rendu natif. Le conteneur vous donne un contrôle beaucoup plus grand sur l'espace, 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 obtient généralement la plupart de la voie sans sur-engineérer les surcharges de thème.
Utilisez une présentation conforme au système d'exploitation
Les systèmes 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 différentes marge, placement d'icône ou mode de sélection.
Les ajustements utiles comprennent :
- Sur iOS : Donnez au champ de la place et prenez en compte la présentation modale par rapport aux étiquettes entourantes.
- Sur Android : Vérifiez la mise en page de texte et la hauteur de défaut du spinner sur plusieurs appareils.
- Pour les deux : Gardez le texte de remplissage visuellement distinct des sélections réelles.
Une comparaison courte aide :
| Préoccupation | iOS | Android |
|---|---|---|
| Comportement ouvert | Sentiment modal | Sentiment de spinner |
| Attentes d'icône | Fonctionnel plus souvent | Ornemental plus souvent |
| Problèmes de mise en page | Disposition du layout de remplacement peut paraître flou | Le texte peut paraître serré |
Si votre interface comprend des dégradés, des cartes superposées ou des surfaces à contrastes élevés, alignez le conteneur de sélection avec le même traitement que vous utilisez ailleurs dans l'interface, de la même manière que les modèles utilisés dans Réactif natif UI travail de dégradé linéaire.
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.
C'est pourquoi le rayon de bord, l'écart des étiquettes et la couleur de la zone de texte de remplissage comptent plus que les personnalisations exotiques du sélecteur.
Scénarios avancés et meilleures pratiques
La plupart des bugs de sélecteur apparaissent après la phase de prototype. Le problème commence lorsque les options proviennent d'un serveur, les règles de validation diffèrent par plateforme et la zone de texte de remplissage ne peut pas agir comme une valeur valide.
Un cas d'edge récurrent est particulièrement ennuyeux sur iOS. Les développeurs ont souvent besoin de charger des valeurs de sélecteur à partir d'un serveur tout en empêchant la zone de texte de remplissage d'être sélectionnable, mais les documents officiels n'abordent souvent pas directement cette voie. La discussion de la communauté souligne quela valeur de la zone de texte de remplissage peut rester sélectionnable sur iOS , ce qui entraîne un comportement incohérent dans les applications réelles, comme le note cette.

Un diagramme illustrant le processus de flux de données à quatre étapes pour utiliser un sélecteur dynamique dans NativeBase.
Charger des options de sélecteur à partir de données de serveur de manière sécurisée : un piège courant que je vois souvent est de traiter les données récupérées comme immédiatement prêtes pour le sélecteur. Gardez une petite couche de transformation entre votre API réponse 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>
);
}
Cette é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.
Prévenir la sélection du champ de remplissage sur iOS
La règle la plus sûre est simple. N'agissez pas comme si le champ de remplissage était une option réelle, même si la bibliothèque de l'interface utilisateur le rend facile à rendre.
Un modèle pratique :
- Utilisez une valeur sentinel vide : Conservez la valeur de remplissage comme
''ou une autre valeur d'application invalide. - Valider avant soumission : Rejetez la valeur vide dans la validation de formulaire, et non seulement dans l'interface utilisateur.
- Désactiver les actions en aval : N'activez pas le bouton de soumission avant qu'une valeur non de remplissage n'existe.
Pour des flux plus stricts, affichez du texte d'aide lorsque le champ de remplissage reste sélectionné :
const isValidSelection = selectedCategory !== '';
Alors, bloquez les boutons d'action ou les appels API en fonction de cette condition au lieu de vous fier à la présentation du sélecteur.
Habitudes d'accessibilité et de production
Les sélecteurs passent souvent inaperçus dans les revues d'accessibilité car ils ressemblent à des éléments natifs. Ils ont cependant besoin de marquages clairs et d'un état prévisible.
Un ou deux habitudes payent rapidement :
- Ajoutez des étiquettes d'accessibilité : Faites en sorte que le champ soit compréhensible par les lecteurs d'écran.
- Gardez les étiquettes explicites : « Pays » est préférable à « Sélectionner ».
- Testez la restauration de l'état : Ré-ouvrir un formulaire et confirmez que l'élément sélectionné précédemment apparaît correctement.
- Écrivez des tests unitaires autour de la logique déterminée par la sélection : La logique en dehors de l'interface utilisateur compte le plus, et unit testing du comportement React vous aide à protéger ces transitions d'état.
Un sélecteur est rarement critique commercialement par lui-même. Les changements d'état derrière lui sont généralement les plus critiques.
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 commerciale hors des appels de rappel du sélecteur fragile et la déplacez dans des effets dérivés d'état, le composant devient beaucoup plus prévisible.
Pour les anciens codebases, ce travail-around est souvent suffisant. Pour les flux critiques, la migration vers Select est généralement une meilleure investissement. Quoi qu'il en soit, 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 pousser des correctifs JavaScript, CSS, copie, configuration et ressources sans attendre la mise à jour de l'app store, Capgo vaut le coup d'œil. Il vous donne des mises à jour en direct signées, un contrôle de déploiement, une protection de rollback et une visibilité de publication pour que vous puissiez récupérer plus rapidement lorsque des bugs de l'interface utilisateur comme les régressions du sélecteur s'échappent en production.