I have a react component, which renders some block with another component based on the state. How could I render both HTML-like JSX and component inside of JS?
class Feedback extends Component {
constructor(props) {
super(props);
this.state = {
storeInputShow: true
};
}
render() {
return (
<div>
{ this.state.storeInputShow ?
<div className="form_field" style={{ marginBottom: '4px' }}>
<Text textTag="div">
Select shop
</Text>
</div>
<Autocomplete
items={this.state.storeList}
shouldItemRender={(item, value) => item.label && item.label.toLowerCase().indexOf(value.toLowerCase()) > -1}
getItemValue={item => item.label}
renderItem={(item, highlighted) =>
<div
key={item.id}
style={{ backgroundColor: highlighted ? '#eee' : '#ffffff', ...selectItemStyle }}
>
{item.label}
</div>
}
value={store}
onChange={e => this.setState({ store: e.target.value, storeId: e.target.id })}
onSelect={(store,storeCard) => this.setState({ store, storeId: storeCard.id })}
inputPlaceholder="Shop"
wrapperStyle={selectWrapperStyle}
menuStyle={selectMenuStyle}
/>
: null
}
</div>
);
}
Right now the error is the following:
SyntaxError: Adjacent JSX elements must be wrapped in an enclosing tag
at the <Autocomplete tag.
The error is self-explanatory, you should wrap both components inside one to make your conditional work.
<div>
<div className="form_field" style... />
<Autocomplete items={this.state.storeList} should... />
</div>
You could use a React.Fragment instead of <div />
BONUS: You could use the following syntax to remove the end null case
<div>
{ this.state.storeInputshow && <YourComponents /> }
</div>
That way you will only render if storeInputShow is available in the state without needing to return null if not.
You can't put if into a return, but you can put a variable.
class Feedback extends Component {
constructor(props) {
super(props);
this.state = {
storeInputShow: true
};
}
render() {
let form;
if (this.state.storeInputShow)
form = <div className="form_field" style={{ marginBottom: '4px' }}>
<Text textTag="div">
Select shop
</Text>
</div>
<Autocomplete
items={this.state.storeList}
shouldItemRender={(item, value) => item.label && item.label.toLowerCase().indexOf(value.toLowerCase()) > -1}
getItemValue={item => item.label}
renderItem={(item, highlighted) =>
<div
key={item.id}
style={{ backgroundColor: highlighted ? '#eee' : '#ffffff', ...selectItemStyle }}
>
{item.label}
</div>
}
value={store}
onChange={e => this.setState({ store: e.target.value, storeId: e.target.id })}
onSelect={(store,storeCard) => this.setState({ store, storeId: storeCard.id })}
inputPlaceholder="Shop"
wrapperStyle={selectWrapperStyle}
menuStyle={selectMenuStyle}
/>;
return (
<div>{ form }</div>
);
Problem in your code is, with in the if-condition, for true case you are returning two elements(,) but in JSX it expects every return to be one element(which can have any number of child's). So just wrap your elements in if-condition with wrapper.
class Feedback extends Component {
constructor(props) {
super(props);
this.state = {
storeInputShow: true
};
}
render() {
return (
<div>
{ this.state.storeInputShow ?
<div>
<div className="form_field" style={{ marginBottom: '4px' }}>
<Text textTag="div">
Select shop
</Text>
</div>
<Autocomplete
items={this.state.storeList}
shouldItemRender={(item, value) => item.label && item.label.toLowerCase().indexOf(value.toLowerCase()) > -1}
getItemValue={item => item.label}
renderItem={(item, highlighted) =>
<div
key={item.id}
style={{ backgroundColor: highlighted ? '#eee' : '#ffffff', ...selectItemStyle }}
>
{item.label}
</div>
}
value={store}
onChange={e => this.setState({ store: e.target.value, storeId: e.target.id })}
onSelect={(store,storeCard) => this.setState({ store, storeId: storeCard.id })}
inputPlaceholder="Shop"
wrapperStyle={selectWrapperStyle}
menuStyle={selectMenuStyle}
/>
</div>
: null
}
</div>
);
}
Related
FoodCreate.js
export class FoodCreate extends Component {
state = {
food: null,
foodList: [],
};
submitFood = (food) => {
this.setState({
foodList: [
...this.state.foodList,
{
key: Math.random(),
name: food,
},
],
});
this.props.navigation.navigate("FoodList", {
foodList: this.state.foodList,
deleteFood: this.deleteFood,
});
};
deleteFood = (key) => {
this.setState({
foodList: [...this.state.foodList.filter((item) => item.key != key)],
});
};
render() {
return (
<Container>
<Header>
<Left>
<Button transparent>
<Icon
name="arrow-back"
onPress={() => this.props.navigation.goBack()}
style={{ fontSize: 25, color: "red" }}
/>
</Button>
</Left>
<Body>
<Title>Add Food</Title>
</Body>
<Right>
<Button transparent>
<Icon
name="checkmark"
style={{ fontSize: 25, color: "red" }}
onPress={() => {
this.submitFood(this.state.food);
}}
/>
</Button>
</Right>
</Header>
<View style={{ alignItems: "center", top: hp("3%") }}>
<TextInput
placeholder="Food Name"
placeholderTextColor="white"
style={styles.inptFood}
value={this.state.food}
onChangeText={(food) => this.setState({ food })}
/>
</View>
</Container>
);
}
}
export default FoodCreate;
FoodList.js
export class FoodList extends Component {
constructor(props) {
super(props);
this.state = {
foodList: [],
};
}
render() {
return (
<Button onPress={() => this.props.navigation.navigate("FoodCreate")}>
Press to insert food
</Button>
<FlatList
data={this.props.navigation.getParam("foodList")} <-------
keyExtractor={(item, index) => item.key.toString()}
renderItem={(data) => <ListItem itemDivider title={data.item.name} />}
/>
);
}
}
export default FoodList;
Hey everyone, I'm building a Diet App, the food gets created in FoodCreate.js by typing a food and pressing the checkmark Icon, and this will create the food and display it in the FoodList.js, keep it in mind that the first Screen to be displayed is FoodList.js. When I run the code I get the following error: undefined is not an object (this.props.navigation.getParam)
try
<FlatList
data={props.route.params.foodList} <-------
...
...
/>
you must see document here: https://reactnavigation.org/docs/params/
I'm trying to create a custom Select using react-select. I want to create a custom Option and SingleValue. The custom Option renders, but SingleValue doesn't. The single value (selected value) displays as per the default styling.
Here's my code,
const Option = (props) => {
const { data, innerProps, innerRef, cx, getStyles, className } = props;
const [hover, setHover] = useState(false);
return (
<div ref={innerRef} {...innerProps}
style={getStyles('option', props)}
className={cx(
{
option: true,
},
className
)}
>
<div style={{ marginLeft: 10}} onMouseEnter={() => setHover(true)} onMouseLeave={() => setHover(false)}>
<p> {data.label} </p>
{ hover ? <Alert bsStyle="success" style={{ opacity: 0.8 }}>
<p> {data.description} </p>
</Alert> : null}
</div>
</div>
)
};
I have tried SingleValue like this,
const SingleValue = ({
cx,
getStyles,
selectProps,
data,
isDisabled,
className,
...props
}) => {
console.log(props);
return (
<div
className={cx(
emotionCss(getStyles("singleValue", props)),
{
"single-value": true,
"single-value--is-disabled": isDisabled
},
className
)}
>
<div>{data.label}</div>
<div style={{ fontSize: "10px" }}>{data.description}</div>
</div>
);
};
And this,
const SingleValue = props => (
<components.SingleValue {...props}>
{props.data.description}
</components.SingleValue>
);
I render it like this,
<Select
id="color"
options={this.props.options}
isMulti={true}
onChange={this.handleChange}
onBlur={this.handleBlur}
value={this.props.value}
components={{ Option, SingleValue }}
/>
Both ways of SingleValue don't work. I have tried to just include SingleValue in components, but that also does't work. Could anyone please help? Thanks!
I had the same problem and I solved it using components.ValueContainer instead of components.SingleValue. In my case, I used react-select to have an icon select component.
This is my constructor:
constructor(props) {
super(props);
this.state = {
icons: [{label: "temperature-low", value: "temperature-low", icon: "temperature-low"}, {label: "utensils", value: "utensils", icon: "utensils"}, {label: "venus", value: "venus", icon: "venus"}, {label: "volume-up", value: "volume-up", icon: "volume-up"}, {label: "wifi", value: "wifi", icon: "wifi"}],
inputValueIcon: "",
};
this.handleChangeIcon = this.handleChangeIcon.bind(this);
}
This is my handleChange function:
handleChangeIcon(selectedItem) {
this.setState({
inputValueIcon: selectedItem
})
}
And finally, my render function:
render() {
const { Option, ValueOption, ValueContainer } = components;
const IconOption = (props) => (
<Option {...props}>
<MDBIcon icon={props.data.label} />
</Option>
);
const ValueOptionLabel = (props) => (
<ValueContainer {...props}>
<MDBIcon icon={props.data.label} />
</ValueContainer>
);
return(
<Fragment>
<Select
placeholder="Select an icon"
value={this.state.inputValueIcon}
onChange={ this.handleChangeIcon }
components={{ Option: IconOption, SingleValue: ValueOptionLabel }}
options={ this.state.icons }
/>
</Fragment>
);
}
It's not the best solution, but it works :)
I have a plus sign which onClick switches to Minus sign. With that, a list of grades is also displayed based on toggle.
Currently when I click on the plug sign, all the elements expand displaying list for all students. I want to only toggle the list for the specific student.
The data is being pulled from an API then being displayed.
class Home extends Component {
constructor(props) {
super(props);
this.state = {
students: [],
search: "",
display: false
};
}
_iconOnClick = event => {
this.state.display ? this.setState({ display: false }) : this.setState({ display: true })
;
};
_renderDataFromAPI = () => {
//search filter
let filteredName = this.state.students.filter(student => {
return (
student.firstName
.toLowerCase()
.includes(this.state.search.toLowerCase()) ||
student.lastName.toLowerCase().includes(this.state.search.toLowerCase())
);
});
return filteredName.map((student,i) => {
let average =
student.grades.reduce((x, y) => parseInt(x) + parseInt(y)) /
student.grades.length;
return (
<div key={i}>
<div className="data">
<img className="img" src={student.pic} align="left" />
{this.state.display ? (
<FontAwesomeIcon
style={{
float: "right",
fontSize: "30px",
marginRight: "40px"
}}
icon="minus"
onClick={this._iconOnClick}
/>
) : (
<FontAwesomeIcon
style={{
float: "right",
fontSize: "30px",
marginRight: "40px"
}}
icon="plus"
onClick={this._iconOnClick}
/>
)}
<h1>
{student.firstName.toUpperCase()} {student.lastName.toUpperCase()}
</h1>
<p>Email: {student.email}</p>
<p>Company: {student.company}</p>
<p>Skill: {student.skill}</p>
<p>Average: {average}%</p>
<br />
{this.state.display ? (
<div >
{student.grades.map((grade, i) => {
return (
<p key={i}>
Test {i + 1}: {grade}%
</p>
);
})}
</div>
) : (
<div />
)}
</div>
<hr style={{ borderColor: "#e2e1e1" }} />
</div>
);
});
};
Move the display to each student.
let dataFromAPI = getDataFromAPI();
this.setState({
students: dataFromAPI.map(
student => Object.assign(student, { display: false })
});
Display the list...
{student.display ? (
<div >
{student.grades.map((grade, i) => { ...
Toggle the show/hide
_iconOnClick = id => {
let students = this.students.map(s => {
if (id == s.id) {
return Object.assign(s, { display: !s.display });
}
return s;
});
this.setState({ students });
};
...
<FontAwesomeIcon
style={{
float: "right",
fontSize: "30px",
marginRight: "40px"
}}
icon="minus"
onClick={this._iconOnClick(student.id)} />
I'm trying to create a list by dynamically adding the list items to an array in the state and then using the map operator to iterate over them. However, the new list items are only rendered after the second click on the button that handles the setState method. Any pointers on resolving this?
...
constructor(props) {
super(props);
this.state = {
requirements:[], // Placeholder array in state
currentRequirement
}
}
...
And in my render method I have this.
{
this.state.requirements.map((el,i) => (
<TouchableOpacity key={i}>
<BulletItem text={el}/>
</TouchableOpacity>
))
}
<FormInput
onChangeText={(value) => {
this.setState({ currentRequirement: value})}
}
placeholder="Enter a new requirement"
/>
<Button
title="Add Requirement"
onPress={() => {
this.onAddRequirementComponent()
}}
/>
The method for handling the setState is this.
onAddRequirementComponent() {
this.setState(previousState => ({
requirements: [...previousState.requirements, this.state.currentRequirement],
currentRequirement:''
}))
}
UPDATE : FULL COMPONENT
import React, { Component } from 'react';
import { Text, View, StyleSheet, ScrollView, Picker, TouchableOpacity } from 'react-native';
import { BulletItem, TagCloud } from "../components/index";
import { Actions } from "react-native-router-flux";
import {
Button,
Header,
FormInput,
FormLabel,
} from 'react-native-elements';
export default class ListScreen extends Component {
constructor(props) {
super(props);
this.state = {
jobtype: '',
level: '',
requirements: [],
benefits: [],
currentRequirement: '',
currentBenefit: ''
}
}
render() {
return (
<View style={styles.container}>
<Header
backgroundColor='#fff'
borderBottomWidth={0}
leftComponent={{ icon: 'corner-up-left', color: '#333', type: 'feather', onPress: () => { Actions.pop() } }}
centerComponent={{ text: 'Create New Job', fontFamily: 'VarelaRound-Regular', style: { color: '#333', fontSize: 18 } }}
rightComponent={{ icon: 'options', color: '#333', type: 'simple-line-icon' }} />
<ScrollView>
<FormLabel>Job Title</FormLabel>
<FormInput placeholder="e.g. Full Stack Javascript Developer"/>
<FormLabel >REQUIREMENTS</FormLabel>
{
this.state.requirements.map((el, i) => (
<TouchableOpacity key={i}><BulletItem containerStyle={{ backgroundColor: '#EFF0F2', borderRadius: 4 }} style={{ backgroundColor: '#EFF0F2' }} text={el} /></TouchableOpacity>
))
}
<FormInput
onChangeText={(value) => {
this.setState({ currentRequirement: value})}
}
placeholder="Enter a new requirement"
/>
<Button
title="Add Requirement"
onPress={() => {
this.onAddRequirementComponent()
}}
/>
<FormLabel style={{ fontFamily: 'VarelaRound-Regular', color: '#333' }} labelStyle={{ fontFamily: 'VarelaRound-Regular', color: '#333' }}>BENEFITS</FormLabel>
{
this.state.benefits.map((el, i) => (
<TouchableOpacity key={i}><BulletItem text={el} /></TouchableOpacity>
))
}
<FormInput value={this.state.currentBenefit} onChangeText={(value) => { this.setState({ currentBenefit: value }) }} placeholder="3 years experience developing Javascript apps" />
<Button title="Add" onPress={() => { this.onAddBenefitComponent() }}/>
<Picker selectedValue={this.state.jobtype}
style={{ height: 50, width: '100%', backgroundColor: '#EFF0F2' }}
onValueChange={(itemValue, itemIndex) => this.setState({ jobtype: itemValue })}>
<Picker.Item label="Full Time" value="fulltime" />
<Picker.Item label="Part Time" value="parttime" />
<Picker.Item label="Contract" value="contract" />
<Picker.Item label="Remote" value="remote" />
</Picker>
<Picker selectedValue={this.state.level}
style={{ height: 50, width: '100%', backgroundColor: '#EFF0F2' }}
onValueChange={(itemValue, itemIndex) => this.setState({ level: itemValue })}>
<Picker.Item label="Junior" value="junior" />
<Picker.Item label="Mid-Level" value="mid" />
<Picker.Item label="Management" value="management" />
<Picker.Item label="Senior" value="senior" />
</Picker>
</ScrollView>
</View>
);
}
onAddRequirementComponent() {
if (this.state.currentRequirement)
this.setState(previousState => ({
requirements: [...previousState.requirements, this.state.currentRequirement],
currentRequirement: ''
}))
}
onAddBenefitComponent() {
this.setState(previousState => ({
benefits: [...previousState.benefits, this.state.currentBenefit],
currentBenefit: ''
}))
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
}
});
This is the core logical part and works perfectly. I used only native elements like TextInput. The only change I made to make the TextInput to full controlled by adding value={this.state.currentRequirement} plus the suggestion of #Tholle for the usage of previousState.
class Test extends Component {
state = {
requirements: [],
currentRequirement: ''
}
onAddRequirementComponent() {
this.setState(previousState => ({
requirements: [...previousState.requirements, previousState.currentRequirement],
currentRequirement:''
}))
}
render() {
return(
<View>
<TextInput onChangeText={(value) => {
this.setState({ currentRequirement: value})}
}
value={this.state.currentRequirement}
/>
{ this.state.requirements.map((el,i) => (
<Text key={i}>{el}</Text>
))}
<Button
title="Add Requirement"
onPress={() => {
this.onAddRequirementComponent()
}}
/>
</View>
);
}
}
Im fairly new to React and i'm trying to create a dropdown where users can add values to the dropdown. Something like this What i want
This is what i got now, but the add button dosent work at all
My dropdown
I had another input field where i could pass the value to the dropdown, but when i tried to implement the logic to the downshift dropdown nothing happened. No error, no value!
Here is my code:
function BasicAutocomplete({ items, onChange }) {
return (
<Downshift
onChange={onChange}
render={({
getInputProps,
getItemProps,
isOpen,
inputValue,
selectedItem,
highlightedIndex,
handleSubmit
}) => (
<div>
<Input {...getInputProps({ placeholder: 'Markedsaktivitet'}) } ref="input" />
{isOpen ? (
<div style={{ border: '1px solid #ccc' }}>
{items
.filter(
i =>
!inputValue ||
i.toLowerCase().includes(inputValue.toLowerCase()),
)
.map((item, index) => (
<div
{...getItemProps({ item }) }
key={item}
style={{
backgroundColor:
highlightedIndex === index ? 'gray' : 'white',
fontWeight: selectedItem === item ? 'bold' : 'normal',
}}
>
{ item }
</div>
))}
<Button type="button" onClick={handleSubmit}><i className="fa fa-plus" /> Add option</Button>
</div>
) : null}
</div>
)}
/>
)
}
class Dropdown extends React.Component {
constructor(props) {
super(props)
this.state = {
inputField: 'no value',
items: ['apple', 'orange', 'carrot']
}
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit() {
const newItem = this.refs.input.value
this.setState({ items: this.state.items.concat(newItem) })
}
render() {
return (
<Wrapper>
<BasicAutocomplete
items={this.state.items}
onChange={selectedItem => console.log(selectedItem)}
onClick={this.handleSubmit}
/>
</Wrapper>
);
}
}
Thanks for the replays!
use bootstrap dropdown menu its good and nice looking
check out
and maybe you find something
Check here there is diffrent model