I want to display on my app a list of meal 'tags'. So based on the code below, I was able to do that. So as a result of the code, I will get a list or a set of mealTags displayed.
Question: I want to only show the first 2 tags, hide the rest and put a link 'show more where the rest will appear when I click it . How can I do this in ReactJS?
return (
<View {...otherprops} style={styles.mainContainer} elevation={3}>
<View style={styles.contentContainer}>
<MealTagsSection mealTags={post.mealTags} />
</View>
type MealTagsProps = {
mealTags: Array<MealTag>;
};
export function MealTagsSection(props: MealTagsProps) {
let {mealTags} = props;
return (
<View style={styles.mealTagsContainer}>
{
mealTags.map((mealTag) => {
let tagStyle = '';
if (mealTag.category === 1) {
tagStyle = styles.tag_healthy;
} else {
tagStyle = styles.tag_improve;
}
return (
<View style={tagStyle}>
<Text style={styles.tagText}>{mealTag.description}</Text>
</View>
);
})
}
</View>
);
}
You can use set visible count in state
this.state= {
visibleCount:2
}
and use slice function before map, for example
mealTags.slice(0, this.state.visibleCount).map(...)
Then you can increase visible count as you want in button onClick funtion.
Another option is to track the index in your .map block
mealTags.map((mealTag, idx) => ...
and style accordingly e.g. display:none when idx >= 2
Related
I have a bug where a user clicks on a survey and then opens up what is called supporting information that expands the UI further, then the user selects his or her answer and clicks on the NEXT QUESTION button, at that point the whole top part of the screen drops down exposing this huge gap. This is the code I believe governs all that behavior:
class BallotSurveyDetails extends PureComponent {
componentDidUpdate(prevProps) {
if (prevProps.currentWizardPage !== this.props.currentWizardPage) {
this.scroll.props.scrollToPosition(0, 0, true);
}
}
render() {
const {
currentWizardPage,
selectedSurvey,
handleNextQuestionButtonPress,
handleResponseChanged,
loading,
responses,
handleSubmitButtonPress,
saving,
wizardPages
} = this.props;
if (!saving && loading) {
return <Loading />;
}
const isWizard = selectedSurvey.Layout !== "Wizard";
const isList = selectedSurvey.Layout !== "List";
const displayNextQ = isWizard && currentWizardPage < wizardPages;
const displaySubmit =
isList || (isWizard && currentWizardPage === wizardPages);
const sortedGroups = (selectedSurvey.QuestionGroups || []).sort(
(a, b) => a.Order - b.Order
);
const wizardGroup = isWizard ? sortedGroups[currentWizardPage - 1] : null;
return (
<SafeAreaView style={styles.container}>
{isWizard && wizardPages.length > 1 && (
<Card style={styles.pagination}>
<RadioPagination
numberOfPages={wizardPages}
currentPage={currentWizardPage}
/>
</Card>
)}
<KeyboardAwareScrollView
showsVerticalScrollIndicator={false}
extraScrollHeight={45}
innerRef={ref => {
this.scroll = ref;
}}
enableOnAndroid={true}
contentContainerStyle={{ paddingBottom: 90 }}
>
<View style={styles.headerContainer}>
<Text style={styles.ballotTitle}>{selectedSurvey.Name}</Text>
<Text style={styles.ballotSubtitle}>
{selectedSurvey.Description}
</Text>
</View>
{isList &&
What I tried to do to resolve this was add automaticallyAdjustContentInsets={false} inside the KeyboardAwareScrollView, did nothing to resolve the bug. Any ideas anyone?
I'm not sure what's causing this for you, but here are a few things that have corrected similar problems I've had in the past:
It can help to wrap every screen in a container with flex:1.
I had a similar case with conditionally rendering a search bar above a FlatList and I used this to fix it:
I added this to the top of my file.
import { Dimensions, other stuff you need} from 'react-native';
const deviceHieght = Dimensions.get('window').height;
and then I wrapped my FlatList in a view like this
<View style={this.state.showBar === false ? styles.containFlatlist : styles.containSearchFlatlist}>
and this is the styling it was referencing
containFlatlist: {
height: deviceHieght
},
containSearchFlatlist: {
height: deviceHieght-100
},
In a different similar case I had an issue like this with a screen that displayed photos on click within a scrollview. In that case I did this:
<ScrollView
ref={component => this._scrollInput = component}
>
Then in componentDidMount I put
setTimeout(() => {
this._scrollInput.scrollTo({ x: 0, animated: false })
}, 100)
I was also using react navigation in this case so I also did
return(<View style={styles.mainFlex}>
<NavigationEvents
onWillBlur={payload => this._scrollInput.scrollTo({x:0})}
/>
Followed by the rest of my code.
I hope one of those helps. Given that you're also dealing with a scrollview, my best guess is that the third fix is most likely to work in your situation.
So the appear is with this code snippet here:
componentDidUpdate(prevProps) {
if (prevProps.currentWizardPage !== this.props.currentWizardPage) {
this.scroll.props.scrollToPosition(0, 0, true);
}
}
In particular, this.scroll.props.scrollToPosition(0, 0, true);. In removing the whole component lifecycle method, the bug went away.
hey guys I am using multiple selector in my app..the thing I want t show is that I want to generate num from 1 to 69 through loop..but when I tried to generate number by loop it gives me error like undefined is not a function ...
I tried a loop as globally and in my render function but every time I have same error...
export default class Createottery extends Component {
static navigationOptions = {
header: null
}
state = { selectedFruits: [],mynum:[] }
onSelectionsChange = (selectedFruits) => {
//alert(JSON.stringify(selectedFruits))
// selectedFruits is array of { label, value }
this.setState({ selectedFruits:selectedFruits })
alert(JSON.stringify(this.state.selectedFruits))
}
componentDidMount(){
for(let mylottery=0; mylottery<=69;mylottery++)
{
this.setState({mynum:mylottery})
//alert(mylottery)
}
}
render () {
let comeon=0
for(comeon=0;comeon<=5;comeon++){
comeon=comeon }
return (
<Container style={styles.Containerstyle}>
<Header searchBar rounded style={styles.headerstyle}>
<Item style={{backgroundColor:'#000'}}>
<Input placeholder="Lottery" placeholderttextSize={22} placeholderTextColor={'#fff'}/>
<Icon name="search" style={{color:'#fff'}} size={22}/>
</Item>
</Header>
<Content>
<View >
<Text style={{color:'#000',alignItems: 'center',fontSize:22}}>select any 5 number or quick pick</Text>
<SelectMultiple
style={{backgroundColor:'black'}}
items={comeon}
//selectedItems={this.state.selectedFruits}
// onSelectionsChange={this.onSelectionsChange}
/>
</View>
</Content>
</Container>
)
I want when my app starts It gives my all number which I want to generate through loop
1) Because you are sending number to items of SelectMultiple Component. items should be array of object.
<SelectMultiple
style={{backgroundColor:'black'}}
items={comeon} {//this should be array instead of number}
selectedItems={this.state.selectedFruits}
onSelectionsChange={this.onSelectionsChange}
/>
2) You are doing the same mistake while creating array in componentDidMount. You are setting every looped number to mynum
I want to get checked friends using checkbox. But I not quite sure how i will achieve it, hope someone can help me.
This is my state:
state = {checked: false}
This is where I want to map array
{this.props.navigation.getParam('friends').map((name, key) => (
<View>
<Text>{name}</Text>
<CheckBox
checked={this.state.checked}
onPress={(val)=>{}}
/>
</View>))}
Note: Or Could someone write me an app/code snippet in snack.expo.io how to get only checked checkbox value
You can write a custom checkbox component
export default class CustomCheckbox extends Component {
constructor(props) {
super(props);
this.state = {
checked: false,
};
}
toggleChange(){
this.setState({checked: !this.state.checked});
}
render() {
return (
<View>
<Text>{this.props.name}</Text>
<CheckBox
checked={this.state.checked}
onPress={() => this.bind.toggleChange(this)}
/>
</View>
);
}
}
and import your CustomCheckbox component
import CustomCheckbox from "your CustomCheckbox.js path"
{this.props.navigation.getParam('friends').map((name, key) => (
<View>
<CustomCheckbox name={name} />
</View>
))}
Your code is pretty good to go, you just need to update a bit. You have following two options:
Your friend's array should have checked key within each containing object, then you can simply do something like this.
{
this.props.navigation.getParam('friends').map((item, key) => (
let {name, checked} = item // item is an object from friends array,the and it have name, checked and other keys
<View>
<Text>{name}</Text>
<CheckBox
checked={checked}
onPress={(val)=>{}}
/>
</View>))
}
Other is you to save the name of the person as key and true/false as the checked state, eg :
toggleCurrentFirendState = (item)=>{
this.setState((prevState)=>{
let {name} = item //get name from clicked friend from the list
return {
...prevState, //used spread operator, so that other states doesn't get mutat.
[name]:!prevState[name] //toogle state of clicked item
}
})
}
//within your render
{
this.props.navigation.getParam('friends').map((item, key) => (
let {name} = item // item is an object from friends array,the and it have name, checked and other keys
<View>
<Text>{name}</Text>
<CheckBox
checked={name ===this.state[name]} //see change
onPress={(val)=>{this.toggleCurrentFirendState(item)}}
/>
</View>))
}
I am creating react-native mobile app. I have an array with some values. I want to set array's value into input field. I have added value in the fields but i can't able to update these values. I have set my values in a qty variable like this:
constructor(props) {
super(props);
this.state = {
qty:[],
}
}
componentWillMount() {
var ids = [];
this.props.data.map((dataImage,Index) => {
dataImage['pro-name'] != undefined && (
ids.push({'qty':dataImage['pro-qty']})
)
})
this.setState({qty:ids})
}
render() {
return {
this.props.data.map((dataImage,Index)=>(
<View key={Index} style={productStyle.cartview}>
{dataImage['pro-name'] && (
<View style={productStyle.cartmain}>
<Input value={this.state.qty[Index]['qty']} onChange={this.handleChangeInput.bind(this)} style={{width:40,height:40,textAlign:'left'}} />
</View>
)}
</View>
))
}
}
Its showing values properly into the input field but i can't able to type anything into the field to update the values. what can i do for this
I will suggest you to move your input container into separate class, its better approach and each component will handle its own state. Its easy to handle and will result better in performance too.
components = []
render() {
return this.props.data.map((item, index) => (
<CustomComponent data={item} index={index} ref={(ref) => this.components[index] = ref} />
))
}
You can then get child (CustomComponent) value from its ref.
this.components[index].getValue()
this.components[index].setValue('Value');
You will need to create these functions (getValue & setValue) in CustomComponent class.
solution
Here is solution to your query. You need to install lodash or find other solution to make a new copy qty.
<Input onChangeText={(text) => this.handleChangeText(text, index)} />
handleChangeText = (text, index) => {
const qty = _.cloneDeep(this.state.qty);
qty[index] = {
qty: text
}
this.setState({qty})
}
Your Input's value is set to this.state.qty[Index]['qty']. And to change it on text edit, you can do it like this. You do not need to bind the function, instead use an arrow function like this.
onChangeText={ (newValue) => {
this.setState({ <your-input-value-state>:newValue })
}}
You have to update the value of each Input individually on onChange event.
Replace your with Input with this
<Input value={this.state.qty[Index]['qty']}
onChange={this.handleChangeInput.bind(this, Index)}
style={{width:40,height:40,textAlign:'left'}}
/>
and update the state accordingly with the Index when the event is called
handleChangeInput(index, value){
let {qty} = this.state;
let qty_update = qty.slice();
qty_update[index]['qty'] = value;
this.setState({qty: qty_update});
}
Is it possible to loop an identical component in Render function?
Something like this:
...
onPress = () => {
...
};
initialArr = [["blue","text1"],["red","text2"]];
buttonsListArr = [];
for (let i = 0; i < initialArr.length; i++)
{
buttonsListArr.push(
<Button style={{borderColor:{initialArr[i][0]}}} onPress={this.onPress.bind(this)}>{initialArr[i][1]}</Button>
);
}
...
render() {
return (
<View style={...}>
{buttonsListArr}
</View>
)};
I mean this is just finite list of components, so any components like ListView/ScrollView etc is not applicable in this particular case. This is just syntax question.
You would usually use map for that kind of thing.
buttonsListArr = initialArr.map(buttonInfo => (
<Button ... key={buttonInfo[0]}>{buttonInfo[1]}</Button>
);
(key is a necessary prop whenever you do mapping in React. The key needs to be a unique identifier for the generated component)
As a side, I would use an object instead of an array. I find it looks nicer:
initialArr = [
{
id: 1,
color: "blue",
text: "text1"
},
{
id: 2,
color: "red",
text: "text2"
},
];
buttonsListArr = initialArr.map(buttonInfo => (
<Button ... key={buttonInfo.id}>{buttonInfo.text}</Button>
);
render() {
return (
<View style={...}>
{initialArr.map((prop, key) => {
return (
<Button style={{borderColor: prop[0]}} key={key}>{prop[1]}</Button>
);
})}
</View>
)
}
should do the trick
For initial array, better use object instead of array, as then you won't be worrying about the indexes and it will be much more clear what is what:
const initialArr = [{
color: "blue",
text: "text1"
}, {
color: "red",
text: "text2"
}];
For actual mapping, use JS Array map instead of for loop - for loop should be used in cases when there's no actual array defined, like displaying something a certain number of times:
onPress = () => {
...
};
renderButtons() {
return initialArr.map((item) => {
return (
<Button
style={{ borderColor: item.color }}
onPress={this.onPress}
>
{item.text}
</Button>
);
});
}
...
render() {
return (
<View style={...}>
{
this.renderButtons()
}
</View>
)
}
I moved the mapping to separate function outside of render method for more readable code.
There are many other ways to loop through list of elements in react native, and which way you'll use depends on what do you need to do. Most of these ways are covered in this article about React JSX loops, and although it's using React examples, everything from it can be used in React Native. Please check it out if you're interested in this topic!
Also, not on the topic on the looping, but as you're already using the array syntax for defining the onPress function, there's no need to bind it again. This, again, applies only if the function is defined using this syntax within the component, as the arrow syntax auto binds the function.
If u want a direct/ quick away, without assing to variables:
{
urArray.map((prop, key) => {
console.log(emp);
return <Picker.Item label={emp.Name} value={emp.id} />;
})
}