Switching between dynamic Pickers crashes app - javascript

I'm new to React Native and need to dynamically populate a Picker depending on the value of a selected Radio Button. For example, in the following code if you select "Animals" the Picker would contain "Dog", "Cat", and "Fish" and if you selected "People" the Picker would contain "Will Smith".
However, if I select "Cat" or "Fish" from Animals and then click on the People radio button, the app crashes. When debugging I saw I was getting an Array Index Out of Bounds Exception, and I'm guessing it's probably because I'm selecting index 1 or 2 in the Animals array, but there is only 1 item in the People array. It must be trying to get index 1 or 2 from the People array, but that is obviously out of bounds.
The behavior I need is for the Picker to go back to the default option "Select one" whenever I change to a different radio button.
import React, { Component } from 'react';
import { View, Picker } from 'react-native';
import RadioGroup from 'react-native-radio-buttons-group';
// Constants for currently selected radio button
const ANIMALS = 0
const PEOPLE = 1
// Constant for when nothing is selected in the Picker
const NOTHING = 'nothing'
export default class App extends Component {
constructor(props) {
super(props)
this.state = {
selectedVal: NOTHING, // Currently selected item
radioType: ANIMALS, // Currently selected radio button
radioValues: [
{
type: ANIMALS,
label: "Animals",
},
{
type: PEOPLE,
label: "People",
},
]
}
}
getCategories = (type) => {
if (type === ANIMALS) {
return [
"Dog", "Cat", "Fish"
]
} else if (type === PEOPLE) {
return [
"Will Smith"
]
}
}
render() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<RadioGroup
flexDirection="row"
radioButtons={this.state.radioValues}
onPress={(data) => {
this.setState({
radioValues: data
})
let selected = this.state.radioValues.find(e => e.selected === true)
if (selected.type === ANIMALS) {
this.setState({ radioType: ANIMALS })
} else if (selected.type === PEOPLE) {
this.setState({ radioType: PEOPLE })
}
}} />
<Picker
selectedValue={this.state.selectedVal}
style={{ height: 50, width: 250 }}
onValueChange={(itemValue) =>
this.setState({ selectedVal: itemValue })
}>
<Picker.Item label="<Select one>" value={NOTHING} />
{
this.getCategories(this.state.radioType).map((categoryName, index) => {
return <Picker.Item label={categoryName} value={categoryName} key={index} />
})
}
</Picker>
</View>
)
}
}
This is what the app looks like:
I tried manually setting this.state.selectedVal back to NOTHING inside of componentDidUpdate() but the app still crashes if I select "Cat" or "Fish" from the Animals view and then switch to People. Strangely it works when I run it from a new Expo project, but not with this project where I used the react-native CLI.
Does anyone know how to fix the array index out of bounds exception?

Related

How can I do dynamic badge on React CoreUI?

I have a problem with a badge on Core UI. I have a Sidebar and one of the elements is Chat. When a new message comes, the badge must show new message. But the old developer have written different ways and I can not change it. I cannot find anything for this.
My codes
Sidebar elements
const _nav = [
{
_tag: "CSidebarNavItem",
name: "Chat",
to: "/chat",
filter: "feedbacks",
icon: "cil-speech",
badge: {
color: "info",
text: "NEW MESSAGE",
},
},
]
My React component
import navigation from "./_nav";
const [filteredNav, setFilteredNav] = useState([]);
const [chatBadge, setChatBadge] = useState(false);
const handleChatBadge = () => {
setChatBadge(true)
}
// it is a sidebar element for user-role
useLayoutEffect(() => {
allwedMenu().then((res) => {
const arr = [navigation[0]];
res.data.items.forEach((element) => {
arr.push(element.name);
});
setFilteredNav(navigation.filter((item) => arr.includes(item.filter)));
});
}, []);
<CSidebarNav>
<CCreateElement
items={filteredNav}
components={{
CSidebarNavDivider,
CSidebarNavDropdown,
CSidebarNavItem,
CSidebarNavTitle,
}}
/>
</CSidebarNav>
I need the badge to show when chatBadge is true. But I don't know how can I write this.
You can only add a condition to show the badge when chatBadge is true.
Based on the Value of ChatBadge, you can use the property of the Component CSideBarNavItem to display the badge and pass the colour and text properties.
Here's the updated code:
<CSidebarNav>
<CCreateElement
items={filteredNav}
components={{
CSidebarNavDivider,
CSidebarNavDropdown,
CSidebarNavItem,
CSidebarNavTitle,
}}
/>
{filteredNav.map((item, index) => (
<CSidebarNavItem
key={index}
name={item.name}
to={item.to}
icon={item.icon}
badge={
item.name === "Chat" && chatBadge
? { color: "info", text: "NEW MESSAGE" }
: null
}
/>
))}
</CSidebarNav>
Hope it helps.

React AntDesign Menu Sub Item ... make it work like component

For the AntD menu ... we utilise <Menu>, <Menu.Item>, <SubMenu>.
But I don't want to use these for navigation but rather for representation. I want to display the attributes of an object using a dropdown as such.
For eg. Apple -> red, fruit; Cucumber -> green, vegetable; would be displayed as a menu with Apple and Cucumber as the Submenu headings, and the dropdowns for each would be red, fruit and green, vegetable respectively.
But I don't want to predefine attributes and Submenu headings. If it was a component (cards for example), I could've made the component render per object, so that if there were 10 objects, 10 cards (for example) would be rendered.
Is it possible to do the same for <SubMenu> and <Menu.Item> where I send the data and it first looks at the key 'Name' and renders as a Submenu Heading and renders attributes individually as Menu Items within the Submenu?
Are there any alternatives I can make use of?
Not sure If my question is very clear ... I'm happy to clarify anything if confused.
Not sure if this is what you want
import React from "react";
import ReactDOM from "react-dom";
import "antd/dist/antd.css";
import "./index.css";
import { Menu } from "antd";
const { SubMenu } = Menu;
const data = [
{
Name: "Apple",
Colour: "red",
Type: "fruit"
},
{
Colour: "green",
Type: "vegetable",
Name: "Cucumber",
Season: "spring"
},
{
Name: "Book",
Title: "hello",
Author: "nick"
}
];
const Sider = () => {
const [openKeys, setOpenKeys] = React.useState(["sub1"]);
const onOpenChange = (keys) => {
const latestOpenKey = keys.find((key) => openKeys.indexOf(key) === -1);
if (data.indexOf(latestOpenKey) === -1) {
setOpenKeys(keys);
} else {
setOpenKeys(latestOpenKey ? [latestOpenKey] : []);
}
};
return (
<Menu mode="inline" onOpenChange={onOpenChange} style={{ width: 256 }}>
{data.map((each) => (
<SubMenu key={each.Name} title={each.Name}>
{Object.entries(each).map(
([key, value]) =>
key !== "Name" && (
<Menu.Item key={each.Name + "-" + key}>{value}</Menu.Item>
)
)}
</SubMenu>
))}
</Menu>
);
};
ReactDOM.render(<Sider />, document.getElementById("container"));
CodeSandbox Demo
Let me know if this works for you

How to apply different styles dynamically for matching text from a dynamic paragraph in React Native

I am doing React Native project. This is my first project in React Native.
And I am getting questions and answers in that (multiple questions and answers).
Each answer has multiple different styles with matching text of answer paragraph.
If any matching text there, I have to apply those styles for that selected texts, There may be different font styles, underline, url link, emails.
I have tried with following code, but, It is displaying empty data.
text: the text to search for in the answer's text
instance: the instance to match in case of multiple instances found within the text (if zero is provided, match all instances)
link: this can be a url or a mailto to use for the matched text
styles: a collection of styles to be applied to the matching text
Json Response is following
{
"question": "How do I change my pwd?",
"answer": "To change your pwd, go to the Settings section from the main menu and choose the Change Password option. The new password will be your new password, as well. Still having difficulty logging in? Please contact the Services Team would be great.",
"format": [
{
"text": "Settings",
"instance": 1,
"link": "",
"styles": {
"fontWeight": "bold"
}
},
{
"text": "Change Password",
"instance": 1,
"link": "",
"styles": {
"fontWeight": "bold"
}
},
{
"text": "Services Team",
"instance": 1,
"link": "mailto:client#gmail.com",
"styles": {
"fontStyle": "underline",
"color": "blue"
}
}
]
}
There may be format key or may not be there. But, If that key there, I have to apply different styles for matching data for answer/question data. Even if there mail id, there, I have to show underline once tap on that, It should open email. If there any any url like website, It should open website on tap of it.
I am showing this data in Flatlist
export default class Cell extends PureComponent {
state = {
isSelected: false,
}
formatedContent = (format, label) => {
let managebleLabel = label; // initialize the working text
format.map((item) => {
const { styles, link, text } = item;
console.log('item', item);
const indexOfText = managebleLabel.indexOf(text); // Get the index of our text
const workingLabel = managebleLabel.substring(0, indexOfText + text.length); // Get complete working label with our text to format
managebleLabel = managebleLabel.split(text)[1]; // This is the left label we are going to work with next
const splittedLabel = workingLabel.split(text); // on position 0 we get the label with no format and on position 1 our text to format
const simpleLabel = <Text>{splittedLabel[0]}</Text>; // create the element
const formatedLabel = link && link.length > 0 ? (
this.isLink(text, link, styles)
) : (
<Text style={typeof styles === Object ? styles : {}}>{text}</Text>
); // Assign the format to label
return (
<Text>
{simpleLabel}
{formatedLabel}
</Text>
); // Join the labels
});
};
isLink = (label, link, style) => {
return (
<TouchableOpacity
onPress={() => Linking.openURL(link)}
style={typeof style === Object ? style : {}}
>
<Text>{label}</Text>
</TouchableOpacity>
);
}
onPress = () => {
const { index, onHeaderSelected, item } = this.props;
this.setState(prevState => ({
isSelected: !prevState.isSelected,
}));
}
render() {
const { isSelected } = this.state;
const { item } = this.props;
const answer = get(faqjson, 'answer');
const formatText = get(faqjson, 'format');
return (
<View style={styles.container}>
<TouchableWithoutFeedback onPress={this.onPress}>
<View style={styles.questionContainer}>
<Text style={styles.question}>{item.question}</Text>
<Image source={isSelected ? res.images.arrow_up : res.images.arrow_down} style={styles.image} />
</View>
</TouchableWithoutFeedback>
{isSelected && (
<View style={styles.answerContainer}>
<Text style={[styles.answer]} ellipsizeMode="tail">
{this.formatedContent(formatText, get(faqjson, 'answer'))}
</Text>
</View>
)
}
</View>
);
}
}
But, It is showing empty data after mapping.
Any suggestions?

react native - change prop state depending on number of buttons selected

I am using a nightlight button library: react-native-selectmultiple-button
In this library there is a prop selected
Description: Type:Boolean. Default is false. The selected prop determines whether the button is selected and highlighted
Is there a way I can change the state of "selected" prop, depending on number of buttons selected?
For example, if I select more than 5 buttons, I want other buttons to be unselectable.
constructor(props) {
super(props)
this.state = {
numberOfbuttonsSelected:0
}
}
{
if(this.state.numberOfbuttonsSelected <5){
<SelectMulipleButton
selected={true}/>}
else{<SelectMulipleButton
selected={false}/>
}
}
The code above won't work any comments or advise would be really appreciated :)
This is the new code:
<View style={{ flexWrap: 'wrap', flexDirection: 'row',backgroundColor:'gray',paddingTop:10,paddingLeft:6,paddingRight:0,borderColor:'white', borderWidth:1}}>
{
multipleData.map(
(interest) =>
<SelectMultipleButton
key={interest}
buttonViewStyle={{
borderRadius: 0,
height: 40,
width: 110,
}}
textStyle={{
fontSize: 15,
}}
highLightStyle={{
borderColor: 'white',
backgroundColor: 'transparent',
textColor: 'white',
borderTintColor: 'white',
backgroundTintColor: '#6AAAC6',
textTintColor: 'white',
}}
multiple={true}
value={interest}
selected={this.state.multipleSelectedData.includes(interest)}
singleTap={valueTap => this.trackSelection(valueTap)} />
)
}
</View>
</ScrollView>
Sorry for the delay in replying. Please see my example component below. I have included explanations in comments inline in the code. Please reach out if you need further help.
export class YourComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
numberOfbuttonsSelected: 0,
multipleSelectedData: []
};
}
//This method is what you mainly need
trackSelection = value => {
if (!this.state.multipleSelectedData.includes(value)) { //This checks if the value already exists in the checked buttons list
if (this.state.numberOfbuttonsSelected < 5) { //Here we check if the number of selected buttons has exceeded the specified number
this.state.multipleSelectedData.push(value);
this.setState({
numberOfbuttonsSelected: this.state.numberOfbuttonsSelected + 1
});
} //else do nothing. Effectively, we are disabling the click on the button.
} else { //we are simply toggling the selection here
this.state.multipleSelectedData.splice(
this.state.multipleSelectedData.indexOf(value), 1
);
this.setState({
numberOfbuttonsSelected: this.state.numberOfbuttonsSelected - 1
});
}
};
render() {
return (
//Customize your render function. I just included one button as an example.
<View>
<SelectMultipleButton
multiple={true}
value={interest} //"interest" is just an example value. Change it according to your requirements for each button.
selected={this.state.multipleSelectedData.includes(interest)}
singleTap={valueTap => this.trackSelection(valueTap)} //valueTap is supposed to be the "value" prop's value for each
//button according to the lib's documentation, but if you're not comfortable using valueTap, you can
//simply pass "interest" (or your own custom value for the particular button) into the trackSelection() method
/>
</View>
);
}
}
EDIT
I went through the code in the lib and the onPress function in the SelectMultipleButton component is why your multiple selection still works:
<TouchableWithoutFeedback
onPress={() => {
if (this.props.multiple) {
this.setState({ selected: !this.state.selected })
this.props.singleTap(this.props.value)
} else {
if (!this.state.selected) {
this.setState({ selected: !this.state.selected })
this.props.singleTap(this.props.value)
}
}
}
}>
I know it's not a good idea to modify library files, but in this case, instead of using the whole lib, you can copy over this file to your project (don't remove the author credit at the top of this file) and add a prop selectable to it and modify the onPress thus:
<TouchableWithoutFeedback
onPress={() => {
if (this.props.multiple) {
if(this.props.selectable) {
this.setState({ selected: !this.state.selected })
this.props.singleTap(this.props.value)
}
} else {
if (!this.state.selected) {
this.setState({ selected: !this.state.selected })
this.props.singleTap(this.props.value)
}
}
}
}>
Pass the prop thus:
<SelectMultipleButton
multiple={true}
value={interest}
selectable={this.state.multipleSelectedData.includes(interest) || this.state.numberOfbuttonsSelected < 5}
selected={this.state.multipleSelectedData.includes(interest)}
singleTap={valueTap => this.trackSelection(valueTap)}
/>
This should solve your problem.

How to set multiple dropdown values to each dynamic element Semantic UI React

I'm having trouble figuring out how to set a dynamic dropdown component with multiple-value selections to each rendered element in a feature I'm working on. I think I'm really close but ultimately need a bit of guidance.
Here's the component:
import React, { Component } from 'react'
import { List, Dropdown, Label } from 'semantic-ui-react'
const directions = [
{key: "0.0", text: "0.0", value: "0.0"},
{key: "22.5", text: "22.5", value: "22.5"},
{key: "45.0", text: "45.0", value: "45.0"},
{key: "67.5", text: "67.5", value: "67.5"},
{key: "90.0", text: "90.0", value: "90.0"}
]
const channels = [
{ch: 65, callsign: "TEST1"},
{ch: 49, callsign: "TEST2"},
{ch: 29, callsign: "TEST3"}
]
export default class DirectionalSelection extends Component {
constructor(props) {
super(props)
this.state = {
channels,
directions,
currentValues: {}
}
}
handleDropdownChange = (e, index, { value }) => {
this.setState(({ currentValues }) => {
currentValues[index] = value
return currentValues
})
}
handleDirAddition = (e, index, { value }) => {
this.setState(({ directions }) => {
directions[index] = [{ text: value, value }, ...this.state.directions]
return directions
})
}
render() {
const { channels, currentValues, directions } = this.state
return (
<div>
<List>
{channels.map((el, index) => (
<List.Item key={index}>
<Label>{el.ch}</Label>
<Dropdown
search
multiple
selection
scrolling
allowAdditions
options={directions}
value={currentValues[index]}
placeholder='Choose directions'
onAddItem={this.handleDirAddition.bind(this, index)}
onChange={this.handleDropdownChange.bind(this, index)}
/>
</List.Item>
))}
</List>
</div>
)
}
}
Right now every time I select dropdown values on any channel, currentValues returns as [object Object]: ["22.5", "45.0"]. I want to set the ch key in channels as the key and the dropdown values array as the value and append them to currentValues.
I hope I've clarified the question enough to understand. Here is a link to Semantic-UI-React docs with the original component I'm using: https://react.semantic-ui.com/modules/dropdown#dropdown-example-multiple-allow-additions. Thanks for the help!
I figured it out! It was so simple, just had to switch the params in handleDropdownChange = (e, index, { value }) to handleDropdownChange = (index, e, { value }). It was setting the event function as the object key.

Categories

Resources