useState one behind the rendered element - reactjs

import React, { useState } from "react";
import { Form } from "react-bootstrap";
import geneList from '../data/SampleSheet.json';
import C3 from 'c3'
import C3Chart from 'c3';
import 'c3/c3.css';
const GENEDATA = [["Gnai3",501, 747, 705, 543, 689], ["genen",3,3,3,3],["Cdc45",22, 30 ,10 ,28, 29]] as [string, ...number[]][];
const GENENAMES = ["Gnai3", "genen", "Cdc45"];
export function Genes(): JSX.Element {
// This is the State (Model)
const [gene, setGene] = useState<([string, ...number[]])>(GENEDATA[0]);
const [geneName, setGeneName] = useState<string>(GENENAMES[0]);
// This is the Control
//no idea why the gene name is one behind but maybe you can figure it out
function updateGene(event: React.ChangeEvent<HTMLSelectElement>) {
setGeneName(event.target.value);
const tempGene = gene;
const changeData = GENEDATA.find((name: [string, ...number[]]): boolean => name[0] === geneName);
if(changeData === undefined){
setGene(tempGene);
alert("Gene not found!");
}
else{
alert(geneName);
setGene(changeData);
}
}
// eslint-disable-next-line #typescript-eslint/no-unused-vars
var chart = C3.generate({
bindto: '#chart',
data: {
columns: [
gene
]
},
axis: {
y: {
label:"Activation Level"
}
},
color: {
pattern: ['#ff7f0e']
},
});
// This is the View
return (
<div>
<Form.Group controlId="Genes">
<Form.Label>Pick gene to display: </Form.Label>
<Form.Select value={geneName} onChange={updateGene}>
{ GENENAMES.map((geneName: string) =>
<option key={geneName} value={geneName}>{geneName}</option>
)}
</Form.Select>
<div id="chart"></div>
</Form.Group>
Selected Gene: {geneName}
</div>
);
}
Im trying to graph some data using the C3 library and the data used in the graph should be synced up with the current geneName, however, it seems that when my page re-renders, the geneName seems to be one ahead of the gene state and I am not sure why.
I thought that maybe it was because state update was being done in on function all together so that the useState wasnt set to the correct value yet which is true. I need my graph data to reflect the current geneName and its data.

I actually figured it out right after creating this post.
It was because i was setting my data name and its respective data equal to the current geneName state. Because the state doesnt change until after the render, it was getting the actual current state which is not what I wanted.
The fix was simply setting the gene to event.target.value insted of geneName
The fix was in my .find

Related

Recursive component rendering based on a tree menu object

I have created a component which adds an additional selection box dropdown whenever a key inside an object is another object.
For example, consider the following object:
{
a1: {
x1: 1,
x2: 2,
x3: 3,
x4: {
z1: "z1",
z2: "z2"
},
x5: [
{
x5a: {
z5a1: 1,
z5a2: 2
}
},
{
x5b: {
z5b1: 1,
z5b2: 2
}
}
]
},
a2: {
x1: 1,
x2: 2,
x3: 3
},
a3: "some values"
};
What I want to achieve is (when I select a value from the dropdown menu):
if subTree[value] is an object ({}) or an array ([]), display its keys or indices in
a new selection box drop down, directly bellow the current
else stop
Initial display
Selecting a value in the dropdown
After I select a value, the next selection will show empty, and so on and so forth...
The problem
When I update a value in a selection box, my code doesn't update/clear the selections bellow it properly.
The source code of my project is available at: https://codesandbox.io/s/frosty-grass-9jdue
When changing a value that is not the last in the path, you need to clear all subsequent selections because they were based on a different path. I'm not quite sure how we do that in your setup because I haven't quite wrapped my head around it.
What makes sense to me is to store the pieces of the path as an array. That way we can use slice to remove the tail. I am going to use lodash's get method as a helper to access the value at a path. I am expecting the prop data to be the object itself rather than Object.entries like you were doing before.
import React, { useState, useEffect } from "react";
import { MenuItem, TextField } from "#material-ui/core";
import _get from "lodash/get";
const InfiniteSelection = ({ data, onCaseCadeSelection }) => {
// an array of segments like ['a1', 'x4', 'z1']
const [path, setPath] = useState([]);
// joins to a string like `a1.x4.z1`
const cascade = path.join(".");
// call callback whenever the cascade changes
useEffect(() => {
if (onCaseCadeSelection) {
onCaseCadeSelection(cascade);
}
}, [cascade]);
// need to know the index in the paths array where the change occurred
const handleChange = (index) => (event) => {
// set this value and delete everything after it
setPath([...path.slice(0, index), event.target.value]);
};
// options for the NEXT value from a given path
const optionsForPath = (path) => {
// lodash get handles this except when path is empty array []
const value = path.length > 0 ? _get(data, path) : data;
// get the options from this path, or null if it is terminal
return typeof value === "object" ? Object.keys(value) : null;
};
// either the current path is to a terminal value, or there should be one more level of selects
const currentOptions = optionsForPath(path);
// helper function can be used as a callback to path.map
// will also be called one extra time for the next values if not on a terminal value
const renderSelect = (value, index) => {
return (
<SelectControlled
className="text form_text"
variant="outlined"
list={optionsForPath(path.slice(0, index)) ?? []}
onChange={handleChange(index)}
value={value ?? ""}
/>
);
};
// render selects for each element in the path and maybe a next select
return (
<div className="vertically_spaced">
{path.map(renderSelect)}
{currentOptions === null || renderSelect("", path.length)}
</div>
);
};
Code Sandbox Link
From #LindaPaiste's answer:
When changing a value that is not the last in the path, you need to clear all subsequent selections because they were based on a different path.
That's the key to solving your problem! You have to somehow blow away and forget everything bellow the selection box whose value you are currently changing.
React was designed around the "blow away and forget" principle. Note also that The Data Flows Down. With that in mind, your task should be fairly easy to complete and while Linda's solution seems to work, it is perhaps not as simple as it could be.
What if we could have a special component that (1) accepts a sub-tree of your data, (2) renders its 1st level children as a selection box dropdown and then (3) repeats the process recursively? Something like this:
<RecursiveComponent subTree={DATA_SAMPLE} {/*maybe some other props*/}/>
When we think of recursion, we have to think of terminal conditions. In our case, this happens when the sub-tree is a primitive type (i.e. not an object ({}) or an array ([])).
Every RecursiveComponent has to:
render the selection menu dropdown, containing all the 1st level children of the sub-tree
render the nested RecursiveComponent, based on props.subTree[selection]
handle user interaction
Something like this:
import { MenuItem, Select } from "#material-ui/core";
import { useState } from "react";
function RecursiveComponent(props) {
const [selection, setSelection] = useState(props.currentSelection);
const handleChange = (event) => {
setSelection(event.target.value);
};
return (
<>
<Select variant="outlined" value={selection} onChange={handleChange}>
{Object.keys(props.subTree).map((key) => (
<MenuItem value={key}>{key}</MenuItem>
))}
</Select>
<div /> {/* forces a line break between selection boxes */}
{props.subTree[selection] !== Object(props.subTree[selection]) ? (
<></>
) : (
<RecursiveComponent
subTree={props.subTree[selection]}
currentSelection=""
/>
)}
</>
);
}
export default RecursiveComponent;
This is how you can use RecursiveComponent in your project by editing index.js:
import { StrictMode } from "react";
import ReactDOM from "react-dom";
import { DATA_SAMPLE } from "./DataSample";
import RecursiveComponent from "./RecursiveComponent";
const rootElement = document.getElementById("root");
ReactDOM.render(
<StrictMode>
<RecursiveComponent subTree={DATA_SAMPLE} currentSelection="" />
</StrictMode>,
rootElement
);

Trigger re-render of subcomponent (react-table) using hooks

I'm still new to React, and functional programming, and Javascript, and JSX, so go easy if this is a stupid question.
I'm modifying one of the example material-ui tables from react-table v7. The original code can be found here. The example is completely functional and is using React Hooks as opposed to classes, as do all of the components of the template I'm using (shout out to creative-tim.com!)
My parent function (representative of a page in my dashboard application), for instance Users.js or Stations.js fetches data from a backend api inside a useEffect hook. That data is then passed as a prop to my subcomponent ReactTables.js
For some reason ReactTables.js does not receive changes to the "data" prop after the parent page's useEffect finishes. However, once I modify the data from a subcomponent of ReactTables (in this case AddAlarmDialog.js) then the table re-renders and all of my data suddenly appears.
How can I trigger the re-render of my subcomponent when data is returned from the parent component's useEffect? I noticed that in older versions of React there was a lifecycle function called componentWillReceiveProps(). Is this the behavior I need to emulate here?
Example Parent Component (Alarms.js):
import React, { useEffect, useState } from "react";
// #material-ui/core components
// components and whatnot
import GridContainer from "components/Grid/GridContainer.js";
import GridItem from "components/Grid/GridItem.js";
import ReactTables from "../../components/Table/ReactTables";
import { server } from "../../variables/sitevars.js";
export default function Alarms() {
const [columns] = useState([
{
Header: "Alarm Name",
accessor: "aName"
},
{
Header: "Location",
accessor: "aLocation"
},
{
Header: "Time",
accessor: "aTime"
},
{
Header: "Acknowledged",
accessor: "aAcked"
},
{
Header: "Active",
accessor: "aActive"
}
]);
const [data, setData] = useState([]);
const [tableType] = useState("");
const [tableLabel] = useState("Alarms");
useEffect(() => {
async function fetchData() {
const url = `${server}/admin/alarms/data`;
const response = await fetch(url);
var parsedJSON = JSON.parse(await response.json());
var tableElement = [];
parsedJSON.events.forEach(function(alarm) {
tableElement = [];
parsedJSON.tags.forEach(function(tag) {
if (alarm.TagID === tag.IDX) {
tableElement.aName = tag.Name;
}
});
tableElement.aTime = alarm.AlarmRcvdTime;
parsedJSON.sites.forEach(function(site) {
if (site.IDX === alarm.SiteID) {
tableElement.aLocation = site.Name;
}
});
if (alarm.Active) {
tableElement.aActive = true;
} else {
tableElement.aActive = false;
}
if (!alarm.AckedBy && !alarm.AckedTime) {
tableElement.aAcked = false;
} else {
tableElement.aAcked = true;
}
//const newData = data.concat([tableElement]);
//setData(newData);
data.push(tableElement);
});
}
fetchData().then(function() {
setData(data);
});
}, [data]);
return (
<div>
<GridContainer>
<GridItem xs={12} sm={12} md={12} lg={12}>
<ReactTables
data={data}
columns={columns}
tableType={tableType}
tableLabel={tableLabel}
></ReactTables>
</GridItem>
</GridContainer>
</div>
);
}
Universal Table Subcomponent (ReactTables.js):
import React, { useState } from "react";
// #material-ui/core components
import { makeStyles } from "#material-ui/core/styles";
// #material-ui/icons
import Assignment from "#material-ui/icons/Assignment";
// core components
import GridContainer from "components/Grid/GridContainer.js";
import GridItem from "components/Grid/GridItem.js";
import Card from "components/Card/Card.js";
import CardBody from "components/Card/CardBody.js";
import CardIcon from "components/Card/CardIcon.js";
import CardHeader from "components/Card/CardHeader.js";
import { cardTitle } from "assets/jss/material-dashboard-pro-react.js";
import PropTypes from "prop-types";
import EnhancedTable from "./subcomponents/EnhancedTable";
const styles = {
cardIconTitle: {
...cardTitle,
marginTop: "15px",
marginBottom: "0px"
}
};
const useStyles = makeStyles(styles);
export default function ReactTables(props) {
const [data, setData] = useState(props.data);
const [columns] = useState(props.columns);
const [tableType] = useState(props.tableType);
const [skipPageReset, setSkipPageReset] = useState(false)
const updateMyData = (rowIndex, columnId, value) => {
// We also turn on the flag to not reset the page
setData(old =>
old.map((row, index) => {
if (index === rowIndex) {
return {
...old[rowIndex],
[columnId]: value
};
}
return row;
})
);
};
const classes = useStyles();
return (
<GridContainer>
<GridItem xs={12}>
<Card>
<CardHeader color="primary" icon>
<CardIcon color="primary">
<Assignment />
</CardIcon>
<h4 className={classes.cardIconTitle}>{props.tableLabel}</h4>
</CardHeader>
<CardBody>
<EnhancedTable
data={data}
columns={columns}
tableType={tableType}
setData={setData}
updateMyData={updateMyData}
skipPageReset={skipPageReset}
filterable
defaultPageSize={10}
showPaginationTop
useGlobalFilter
showPaginationBottom={false}
className="-striped -highlight"
/>
</CardBody>
</Card>
</GridItem>
</GridContainer>
);
}
ReactTables.propTypes = {
columns: PropTypes.array.isRequired,
data: PropTypes.array.isRequired,
tableType: PropTypes.string.isRequired,
tableLabel: PropTypes.string.isRequired,
updateMyData: PropTypes.func,
setData: PropTypes.func,
skipPageReset: PropTypes.bool
};
**For the record: if you notice superfluous code in the useEffect it's because I was messing around and trying to see if I could trigger a re-render.
I dont know exactly how the reactTable is handling its rendering, but if its a pure functional component, then the props you pass to it need to change before it will re-evaluate them. When checking if props have changed, react will just do a simple === comparison, which means that if your props are objects whos properties are being modified, then it will still evaluate as the same object. To solve this, you need to treat all props as immutable
In your example, you are pushing to the data array, and then calling setData(data) which means that you are passing the same instance of the array. When react compares the previous version of data, to the new version that you are setting in the call to setDate, it will think data hasnt changed because it is the same reference.
To solve this, you can just make a new array from the old array by spreading the existing array into a new one. So, instead of doing
data.push(tableElement);
You should do
const newInstance = [...data, tableElement];
Your code will need some tweaking because it looks like you are adding in lots of tableElements. But the short version of the lesson here is that you should never try and mutate your props. Always make a new instance
EDIT: So, after looking again, I think the problem is the way you are using the default param in the useState hook. It looks like you are expecting that to set the state from any prop changes, but in reality, that param is simply the default value that you will put in the component when it is first created. Changing the incoming data prop doesn't alter your state in any way.
If you want to update state in response to changes in props, you will need to use the useEffect hook, and set the prop in question as a dependancy.
But personally, I would try and not have what is essentially the same data duplicated in state in two places. I think the best bet would be to store your data in your alarm component, and add a dataChanged callback or something which will take your new data prop, and pass it back up to alarm via a parameter in the callback

How to write Test cases for useEffect Hook in React using Jest & Enzyme?

import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
const InputComponent = ({ item, data }) => {
const [value, setValue] = useState('');
// Binding values for Edit based on unique Key
useEffect(() => {
if (data && data[item.field] && data[item.field] !== 'undefined') {
setValue(data[item.field]);
}
}, [data,item.field]);
//On change setting state
const setState = e => {
setValue(e.target.value);
};
return (
<div className='Input-Containter' data-test='InputBox-tbl-evt'>
<input
data-test='input-ele'
type='text'
value={value}
onChange={e => setState(e)}
/>
</div>
);
};
InputComponent.propTypes = {
item: PropTypes.object,
data: PropTypes.object
};
InputComponent.defaultProps = {
data: {
id: '1',
name: 'd'
},
item:{
field: 'id',
}
};
export default InputComponent;
Can someone help me How to test for setValue() in useEffect
-> Updated complete code for this Component
-> Component will take some data for binding values into input element & in the same
component we can edit values in it as-well.
First, let's take closer look onto useEffect section. What does it mean?
if any of prop is changed
and combination of new values are given some meaningful value(not undefined)
we initialize input's value based on those props even if we have to override custom value
How to test that? Changing prop(s) and validating input's value.
Based on that we may have up to 3(changed only first prop, only second or both) * 2 (if result is undefined or not) * 2 (if there has been already custom value provided and stored in useState or not) = 12. But I believe exhaustive testing is not a good way. So I'd put most checks in single test case:
it('re-init value for nested input after props changes', () => {
const wrapper = mount(<InputComponent />);
function getInput(wrapper) {
return wrapper.find("input").prop("value");
}
expect(getInput(wrapper).props("value")).toEqual("1"); // based on default props
getInput(wrapper).props().onChange({ target: {value: "initial"} }); // imitating manual change
expect(getInput(wrapper).props("value")).toEqual("initial");
wrapper.setProps({data: {a: "some-a", b: "undefined"} });
expect(getInput(wrapper).props("value")).toEqual("initial");
wrapper.setProps({ item: { field: "c" } }); // data[item.field] is undefined
expect(getInput(wrapper).props("value")).toEqual("initial");
wrapper.setProps({ item: {field: "b" } }); // data[item.field] is "undefined"
expect(getInput(wrapper).props("value")).toEqual("initial");
wrapper.setProps({ item: {field: "a" } }); // data[item.field] is meaningful
expect(getInput(wrapper).props("value")).toEqual("some-a");
});
As for getValue helper - it's needed cause we cannot just memoize input element itself like:
const wrapper = mount(...);
const input = wrapper.find("input");
...
expect(input.prop("value")).toEqual();
...
expect(input.prop("value")).toEqual();
Details can be found in Enzyme's docs. Or just know we need to re-run find after any update.
Also beware Enzyme's setProps does not replace current props but update them by merging(as well as React's setState does with state).

React: useState filter array not updating state

Edit:
My error occured because I passed an array as a second parameter to useEffect. Even though the values inside the array stayed the same, the reference changed constantly, therefore useEffect was called constantly and reset my checkbox values. That array was created by an useState call. I replaced useState by useReducer (reducer only changes the object reference if the object is actually changed) and updated some missing dependencies higher up the component tree.
Original question:
I have trouble updating a state in a functional component.
My question is somewhat similiar to this one:
React SetState doesn't call render
I'm already copying my state object (by using array.filter) instead of referencing it; but my state still doesn't update.
In order to track down the problem, I tried re-creating the problem in a minimal example:
jsfiddle
But in my minimal example, everything works as expected. I'm unable to reproduce the error.
Here is my example where the state doesn't update:
configCheckboxGroup.tsx:
import classNames from "classnames";
import React, { useState, useEffect } from "react";
import { Component } from "../../model";
import CheckboxPanel from "./panels/checkboxPanel";
interface configCheckboxGroupProps {
className?: string;
choices: Array<Component>;
selected: Array<string>;
addToCart: (items: Array<Component>) => void;
}
const ConfigCheckboxGroup: React.SFC<configCheckboxGroupProps> = ({
className,
choices,
selected,
addToCart,
}) => {
const [ selectedComp, setSelectedComp ] = useState<Array<string>>(selected);
// device loads later, selected has to be updated
useEffect(() => {
setSelectedComp(selected);
}, [selected]);
const handleOnChange = (ev: React.FormEvent, id: string) => {
console.debug(id);
console.debug(selectedComp.filter(el => el !== id));
if (selectedComp.includes(id)) {
// was already checked || this line is not working!
setSelectedComp(selectedComp.filter(el => el !== id));
} else {
// was not checked
setSelectedComp([...(selectedComp), id]);
}
const selected = choices.filter(el => selectedComp.includes(el.reference._id));
addToCart(selected);
};
return (
<div className={classNames("panellist", className)}>
{
choices.map(el => {
return (
<CheckboxPanel
image={ el.reference.picture ? el.reference.picture : undefined }
name={ el.reference.name }
id={ el.reference._id }
price={ el.reference.price ? el.reference.price :
el.price ? el.price : 0 }
key={ el._id }
checked={ selectedComp.includes(el.reference._id) }
onChange={ handleOnChange }
/>
)
})
}
<span>
{ selectedComp }
</span>
</div>
)
}
export default ConfigCheckboxGroup;
And checkboxPanel.tsx:
import classNames from "classnames";
import React from "react";
import "./checkboxPanel.scss";
import noImage from "../../../resources/images/errors/no-image.svg";
interface PanelProps {
className?: string;
image?: string;
name: string;
id: string;
price: number;
checked: boolean;
onChange: (ev: React.FormEvent, id: string) => void;
}
const CheckboxPanel: React.SFC<PanelProps> = ({
className,
image,
name,
id,
price,
checked,
onChange,
}) => {
const getImage = () => {
if (image) {
return image;
} else {
return noImage;
}
}
return (
<div className={classNames("panel", "checkbox-panel", className)}>
<div className="top">
<div className="image">
<img alt="Product" src={getImage()} />
</div>
<div className="name">
{name}
</div>
</div>
<div className="bottom">
<div className="category">
{ Number(price).toFixed(2) } €
</div>
<div>
<input type="checkbox"
checked={ checked }
onChange={ (e) => onChange(e, id) }
/>
</div>
</div>
</div>
)
};
export default CheckboxPanel;
The only difference between the examples is that in the second one, I call the handle function inside a child component. But I do the same thing on other occasions as well: I have a very similar Component configRadioGroup with radio buttons instead of checkboxes where everything works fine.
I tried playing around by manually filtering the array and trying a lot of other things, but nothing seemed to help. This is why, as a last try, I ask here (although I know that this question is not a good one due to it being very specific).
Changing the prop selected will reset selectedComp if you put a console log in your useEffect you may find that that is resetting it every time.
You need to track down where selected comes from (redux?) and how it's set (addToCart?).
A dirty fix could be to only set selectedComp when component mounts, this is dirty and will/should cause react-hooks/exhaustive-deps lint to trigger:
useEffect(() => {
setSelectedComp(selected);
}, []);
But better to track down what's going wrong with selected, if it comes from redux then maybe just use selected instead and forget about selectedComp since that is just a copy.

Angular 1.X ng-options equivalent using React

Angular 1.X has ng-options for the choices in a select dropdown, each item being an object. In plain HTML, the value of an option can only be a string. When you select one option in Angular, you can see the actual selected object while in plain html, you can only get that string value.
How do you do the equivalent of that in React (+Redux)?
I came up with a solution that does not use JSON.stringify / parse for the value of the select React element nor does it use the index of the array of choice objects as the value.
The example is a simple select dropdown for a person's gender -- either male or female. Each of those choices is an actual object with id, text, and value properties. Here is the code:
MySelect component
import React, { Component } from 'react';
class MySelect extends Component {
onGenderChange = (event) => {
// Add the second argument -- the data -- and pass it along
// to parent component's onChange function
const data = { options: this.props.options };
this.props.onGenderChange(event, data);
}
render() {
const { options, selectedOption } = this.props;
// Goes through the array of option objects and create an <option> element for each
const selectOptions = options.map(
option => <option key={option.id} value={option.value}>{option.text}</option>
);
// Note that if the selectedOption is not given (i.e. is null),
// we assign a default value being the first option provided
return (
<select
value={(selectedOption && selectedOption.value) || options[0].value}
onChange={this.onGenderChange}
>
{selectOptions}
</select>
);
}
}
App component that uses MySelect
import _ from 'lodash';
import React, { Component } from 'react';
class App extends Component {
state = {
selected: null
}
onGenderChange = (event, data) => {
// The value of the selected option
console.log(event.target.value);
// The object for the selected option
const selectedOption = _.find(data.options, { value: parseInt(event.target.value, 10) });
console.log(selectedOption);
this.setState({
selected: selectedOption
});
}
render() {
const options = [
{
id: 1,
text: 'male',
value: 123456
},
{
id: 2,
text: 'female',
value: 654321
}
];
return (
<div>
<label>Select a Gender:</label>
<MySelect
options={options}
selectedOption={this.state.selected}
onGenderChange={this.onGenderChange}
/>
</div>
);
}
}
Lodash is used to look up the choice object in the array of choice objects inside the onGenderChange function in the App component. Note that the onChange passed to the MySelect component requires two arguments -- an extra data argument is added in order to be able to access the choice objects ("options"). With that, you can just set the state (or call an action creator if using Redux) with the choice object for the selected option.
I run into this same situation while migrating a angular 1 app to react. And I felt it is a much needed feature that I couldn't find so here I leave my implementation of NgOption in react using bootstrap (you can can change that to whatever you are using) for anybody that's missing the goo'old angular 1:
import React from 'react';
import { Form, InputGroup } from 'react-bootstrap';
interface props<T, U>{
options: Array<T>,
selected?: U,
onSelect: (value: T) => any,
as: (value: T) => string,
trackBy: (value: T) => U,
disabled?: boolean
}
type state = {}
export default class NgOptions extends React.Component<props<any, any>, state> {
componentDidMount() {
if (this.props.selected) {
this.props.onSelect(this.props.options.find((o) => this.props.trackBy(o)===this.props.selected))
}
}
public render() {
return ( <InputGroup>
<Form.Control as="select"
disabled={this.props.disabled}
onChange={(e) => this.props.onSelect(this.props.options.find((o) => this.props.trackBy(o)===e.target.value))}>
{this.props.options.map( option =>
<option key={this.props.trackBy(option)}
selected={this.props.selected===this.props.trackBy(option)}
value={this.props.trackBy(option)}>{this.props.as(option)}</option>
)}
</Form.Control>
</InputGroup>
)
}
}

Resources