i'm new in react native, i want to add Form if dropdown clicked. i use ModalDropdown library. but, i'm confuse to add that. thanks
this is my code
const OPTION_STATUS = ['option1', 'option2', 'option3'];
class.....{
return(
<ModalDropdown
options={this.state.status_option}
defaultIndex={-1}
defaultValue={'Please select Status Update'}
onDropdownWillShow={this._status_willShow.bind(this)}
onDropdownWillHide={this._status_willHide.bind(this)}
onSelect={(idx, value) => this._status_onSelect(idx, value)}
/>
);
_status_willShow() {
setTimeout(() => this.setState({
status_option: OPTION_STATUS,
}), 2000);
}
_status_willHide() {
this.setState({
status_option: null,
});
}
_status_onSelect(idx, value) {
console.debug(`idx=${idx}, value='${value}'`);
this.setState({status: value});
}
}
You need to toggle the form in some way inside your _status_onSelect function.
Example:
_status_onSelect(idx, value) {
console.debug(`idx=${idx}, value='${value}'`);
this.setState({status: value, showForm: true});
}
And then you can add the UI elements in your render method like so:
{this.state.showForm ?
<View>
// Your form content here
</View>
: null }
Related
I'm new in RN. When I want to navigate between screens I create this function:
displayScreen2 = () => {
this.props.navigation.navigate("screen2")
}
and I call it in onPress={this.displayScreen2}
with TouchableOpacity or any Touchable when the user clicks he has to wait 1 second or 2 before displaying the screen. So what I want is to change the Touchable icon to an loader.
It's simple if I use a conditional rendering but I don't know how to do it now, when I have to change my state? Any suggestions?
this is my approach:
<TouchableOpacity
style={Styles.topButton}
onPress= {() => {
this.setState({loading: 'load'},
() => {
displayScoreListView()
// this.setState({loading: 'icone'})
}
)
}}
>
<Text style={Styles.scoreListButtonTextRed}>{this.state.loading}</Text>
that not work, tha state change but visualy not because if I return to the first screen I have 'load' in the text component
You could create a custom component wrapping whatever Touchable you prefer, I've used this technique in my production apps before. The button has it's own state which allows you to automatically display a loading indicator when necessary.
export class ButtonWorker extends Component {
state = {
working: false
}
onButtonPress = () => {
this.setState(
{ working: true },
() => {
this.props.onPress(this.onWorkFinished);
}
);
}
onWorkFinished = () => {
this.setState({ working: false });
}
render() {
return (
<TouchableOpacity>
{this.state.working ? (
<ActivityIndicator />
) : (
this.props.children
)}
</TouchableOpacity>
);
}
}
And then use it like a normal button with additional logic!
export class NavigationScreen extends Component {
navigate = (done) => {
// ... Asynchronous logic goes here
done();
this.props.navigation.navigate("Screen2");
}
render() {
return (
<Fragment>
{/* ... */}
<ButtonWorker onPress={this.navigate} />
</Frament>
);
}
}
I'm having issue with my componentDidUpdate always updating the render()/state.
My state by default has applicantsData
this.state = {
applicantsData: []
};
ComponentDidMount and ComponentDidUpdate call my method that loads data for the state
componentDidMount() {
this.getApplicants();
}
componentDidUpdate(prevProps) {
// needs to be a unique value
if (prevProps.applicantsData !== this.props.applicantsData) {
this.getApplicants();
}
}
getApplicants = async () => {
AsyncStorage.getItem('CurrentPropertyID')
.then((value) => {
const propID = JSON.parse(value);
this.props.getApplicants(propID);
this.setState({ applicantsData: this.props.applicantsData });
});
}
Then, I have a search box and a FlatList to create my component. I'm using react-native-search-box' for the search box
<Search
ref="search_box"
onSearch={this.onSearch}
onChangeText={this.onChangeText}
/>
<FlatList
extraData={this.state}
data={this.state.applicantsData}
keyExtractor={(item) => {
return item.id.toString();
}}
renderItem={this.renderItem}
/>
My onChangeText method:
onChangeText = (searchText) => {
return new Promise((resolve) => {
let data = this.props.applicantsData;
if (searchText !== '') {
data = data.filter((item) =>
item.name.toUpperCase().includes(searchText.toUpperCase()) ||
item.Email.toUpperCase().includes(searchText.toUpperCase()) ||
item.Phone.toUpperCase().includes(searchText.toUpperCase())
).map(({ id, name, dtEntered, approve, Email, Phone, image }) =>
({ id, name, dtEntered, approve, Email, Phone, image }));
this.setState({ applicantsData: data });
} else {
this.setState({ applicantsData: this.props.applicantsData });
}
resolve();
});
}
Everything is working fine. The data is loaded and displayed in the screen. However, the componentDidUpdate is called all the name and keep updating the props, so when I use the search box to filter my state.applicantsData, it is quickly filtered, then all the data is loaded again because the this.getApplicants was called inside the componentDidUpdate().
Does it make sense? How can I fix this issue?
Thanks
This part is the problem.
componentDidUpdate(prevProps) {
// needs to be a unique value
if (prevProps.applicantsData !== this.props.applicantsData) {
this.getApplicants();
}
}
You have to use deep compare method to compare applicantsData.
You can use https://www.npmjs.com/package/deep-equal or isEqual from lodash
I want to support localization using react-18next.
This component shows a Picker, sets a LocalStorage key (the selected language) and change the app language.
I noticed the onValueChange method is called twice. The first call (using a proper selection tap action on a Picker item) have the correct parameter (the language I have chosen), the second call have the value of the first Picker item, whenever the values is (!).
Example: if I select the English Picker item, I see the Picker switch to English (first _changeLanguage() call), and then again to "Device" (second _changeLanguage() call). I'm sure it's an async ops problem, not sure where..
#translate(['settings', 'common'], { wait: true })
export default class Settings extends Component {
state = {};
constructor(props) {
super(props);
}
componentWillMount() {
this.getLang();
}
async _changeLanguage(ln) {
const { t, i18n, navigation } = this.props;
console.warn("_changeLanguage: ",ln)
await this.promisedSetState({lang:ln})
if(ln=="device") {
console.warn("removing lang setting")
await AsyncStorage.removeItem('#App:lang');
} else {
console.warn("lang setting: ", ln)
await AsyncStorage.setItem('#App:lang', ln);
i18n.changeLanguage(ln)
}
};
//get Language from AsyncStorage, if it has been previously set
async getLang() {
const value = await AsyncStorage.getItem('#App:lang');
console.warn("getLangfrom asyncstorage:", value)
if(value) await this.promisedSetState ({lang:value})
}
promisedSetState = (newState) => {
return new Promise((resolve) => {
this.setState(newState, () => {
resolve()
});
});
};
render() {
const { t, i18n, navigation } = this.props;
const { navigate } = navigation;
return (<View>
<Picker
selectedValue={this.state.lang}
onValueChange={(itemValue, itemIndex) =>this._changeLanguage(itemValue) }>
<Picker.Item color="#666" label="detected from device" value="device" />
<Picker.Item label="English" value="en" />
<Picker.Item label="Deutsch" value="it" />
</Picker>
</View>);
}
}
The code is based on the react-i18next Expo example
https://github.com/i18next/react-i18next/tree/master/example/v9.x.x/reactnative-expo
I'm not sure if this belongs here but if anyone is encountering the problem of onValueChange firing twice and is using react-native-picker-select instead of the default Picker then this solution may work for you.
I ended up using the itemKey property instead of value and setting the key of each item to be the same as its value. Note the example item would end up shaped like so: { key: 'value1', value: 'value1', label: 'Your Label' } where the key and value are identical.
Original Code:
<RNPickerSelect
style={styles.picker}
value={value}
onValueChange={this.onValueChange}
placeholder={placeholder}
items={options}
/>
Fixed Code:
<RNPickerSelect
style={styles.picker}
itemKey={value}
onValueChange={this.onValueChange}
placeholder={placeholder}
items={options}
/>
I got my inspiration from this: GitHub issue.
Searching around the react-native Picker seems bugged.
This quickfix solved my problem https://github.com/facebook/react-native/issues/13351#issuecomment-450281257
I try to get which is not active (in term of NativeBase.io - https://docs.nativebase.io/Components.html#button-def-headref, which simply means that it has no background color) and after I click it, it becomes active (it has a background color).
I define button like this:
<Button active={this.state.selected} onPress={() => this.select()} first>
<Text>Puppies</Text>
</Button>
selected variable in my state is by default false. When I run the application, it works correctly.
The select() method is implemented:
select() {
this.setState({ selected: true })
}
I expect that after I click on the button, it should change its background but it isn't. I check the value of this.state.selected and it changes appropriately. What I'm doing wrong?
export default class MyComponent extends Component {
state = {
selected: false
}
handlePress = () => {
const { selected } = this.state;
this.setState({
selected: !selected,
})
}
render() {
const { selected } = this.state;
return (
<Button active={selected} onPress={this.handlePress} first>
<Text>Puppies</Text>
</Button>
);
}
}
Expo-cli: 2.2.0
React-Navigation: 2.18.0
I have the following two screens for React Navigation, where one is to input the form details and another is the screen where the user can either edit on the submissions or confirm.
My Input interface looks like this:
export default class PickDropInterface extends
React.Component<NavigationProps<>> {
this.state = { tasks: [],
}
onSubmit = () => { this.props.navigation.navigate("Confirmation",
{tasks: this.state.tasks, deleteItem: this.deleteItem.bind(this)}); }
deleteItem = (key) => { var filteredTasks =
this.state.tasks.filter(function (item) { return (item.key !==key);
});
render() {
return (
<ItemDetail onSubmit={this.onSubmit} /> ) }
My Confirmation screen looks like this:
export default class Confirmation extends React.Component<NavigationProps<>> {
this.state={
refresh: false,
}
deleteItem = (key) => {
this.props.navigation.state.params.deleteItem(key);
this.setState({
refresh: !this.state.refresh
})
}
_renderItem =({ item }) =>
(
<View style={styles.cardStyle}>
<Button
primary
label="Delete" onPress= {() => {this.deleteItem(item.key)}} /></View>
)
render() {
return (
<FlatList data={task}
renderItem= {this._renderItem}
keyExtractor= {(item) => item.key.toString()}
extraData={this.state} />
)
}
Expected Output:
The delete button to prompt refresh in the FlatList and show the new Task list.
Current Output:
https://www.youtube.com/watch?v=RmrurTBQpak&feature=youtu.be
I don't know why FlatList didn't re-render, but I found a much simpler solution to what I wanted.
I used conditional rendering instead and I kind of think it's the way to do it instead of navigating to the other screen.
What I did is:
I made a new state called 'orderComplete' and set it to false as default.
Whenever, it is 'false', I made inputInterface above to render whereas it was 'true', I made the above ConfirmationScreen render.
More on Conditional Rendering can be found in React's official documentation.
FlatList above works like a charm now.