RadioGroup Onchange is not firing in React - reactjs

I am unable to find the root cause of why onChange event is not firing after changing the check, however, when component first loads onChange gets called.
I have prepared a CodeSandbox with some sample data.
CodeSandBox link
Here is the component ,
import React from "react";
import styled from "styled-components";
import { checklists } from "./checklists";
import { questions } from "./questions";
const RadioOption = styled.input`
margin: 10px;
width: 16px;
height: 16px;
`;
const RadioGroup = (props) => {
return React.Children.map(props.children, (child) => {
if (child.type === RadioOption)
return React.cloneElement(child, {
type: "radio",
defaultChecked: props.value === child.props.value,
name: props.name,
disabled: props.disabled,
onChange: props.handleChange
});
return child;
});
};
class RadioGroupDemo extends React.Component {
state = {
checklist: "test_checklist_checklist",
questions: questions,
checklists: checklists
};
constructor(props) {
super(props);
this.onRadioChange = this.onRadioChange(this);
this.keyCount = 0;
this.getKey = this.getKey.bind(this);
}
onRadioChange = (e) => {
debugger;
console.log("handleRadios", e);
//let q = this.state.questions;
};
getKey() {
return this.keyCount++;
}
//first render
render() {
console.log(this.state.checklists);
return (
<div style={{ maxHeight: "600px", overflow: "auto" }}>
{this.state.checklist &&
this.state.checklists.questions[this.state.checklist].map(
(question) => {
//debugger;
let question_id = question.question_id;
return (
<div key={`${this.state.checklist}_${question.id}`}>
{question.title}
<div style={{ width: "120px", flex: "0 0 120px" }}>
{(() => {
if (
typeof this.state.questions[question.id] !== "undefined"
) {
return (
<RadioGroup
name={`field_question_${question.id}`}
disabled={this.state.readOnly}
value={
typeof this.state.questions[question.id].answer ==
"undefined"
? this.state.questions[question.id].answer
: Object.keys(this.state.questions)
.reduce(
(arr, key) =>
arr.concat(this.state.questions[key]),
[]
)
.find((q) => {
return q.question_id === question_id;
}).answer
}
onChange={this.onRadioChange}
>
<RadioOption key={this.getKey()} value="yes" />
<RadioOption key={this.getKey()} value="no" />
<RadioOption key={this.getKey()} value="na" />
</RadioGroup>
);
}
})()}
</div>
</div>
);
}
)}
</div>
);
}
}
export default RadioGroupDemo;
Please take a look at the codesandbox and tell me why the event is not firing.
Thanks

Issue
You pass an onChange prop to RadioGroup but access a handleChange prop that doesn't exist.
const RadioGroup = (props) => {
return React.Children.map(props.children, (child) => {
if (child.type === RadioOption)
return React.cloneElement(child, {
type: "radio",
defaultChecked: props.value === child.props.value,
name: props.name,
disabled: props.disabled,
onChange: props.handleChange, // <-- accessed as handleChange
});
return child;
});
};
In component
<RadioGroup
name={`field_question_${question.id}`}
disabled={this.state.readOnly}
value={
typeof this.state.questions[question.id].answer ==
"undefined"
? this.state.questions[question.id].answer
: Object.keys(this.state.questions)
.reduce(
(arr, key) =>
arr.concat(this.state.questions[key]),
[]
)
.find((q) => {
return q.question_id === question_id;
}).answer
}
onChange={this.onRadioChange} // <-- passed as onChange
>
In RadioGroupDemo you also don't bind this to the onRadioChange handler. This binding isn't necessary though since onRadioChange is declared as an arrow function, this is bound automatically.
constructor(props) {
super(props);
this.onRadioChange = this.onRadioChange(this); // <-- here
this.keyCount = 0;
this.getKey = this.getKey.bind(this);
}
Solution
Remove attempt to bind this to handler in constructor.
constructor(props) {
super(props);
this.keyCount = 0;
this.getKey = this.getKey.bind(this);
}
Access the correct prop.
const RadioGroup = (props) => {
return React.Children.map(props.children, (child) => {
if (child.type === RadioOption)
return React.cloneElement(child, {
type: "radio",
defaultChecked: props.value === child.props.value,
name: props.name,
disabled: props.disabled,
onChange: props.onChange // <-- onChange
});
return child;
});
};

Because in the RadioOption. You are using props.handleChange
So in RadioGroupDemo, just update onChange to handleChange
<RadioGroup
...
handleChange={this.onRadioChange}
>

Related

setting ref on functional component

I am trying to change this class based react component to a functional component but i am gettig an infinite loop issue on setting the reference, i think its because of on each render the ref is a new object.
How could i convert the class based component to a functional component
index-class.js - Ref
class Collapse extends React.Component {
constructor(props) {
super(props);
this.state = {
showContent: false,
height: "0px",
myRef: null,
};
}
componentDidUpdate = (prevProps, prevState) => {
if (prevState.height === "auto" && this.state.height !== "auto") {
setTimeout(() => this.setState({ height: "0px" }), 1);
}
}
setInnerRef = (ref) => this.setState({ myRef: ref });
toggleOpenClose = () => this.setState({
showContent: !this.state.showContent,
height: this.state.myRef.scrollHeight,
});
updateAfterTransition = () => {
if (this.state.showContent) {
this.setState({ height: "auto" });
}
};
render() {
const { title, children } = this.props;
return (
<div>
<h2 onClick={() => this.toggleOpenClose()}>
Example
</h2>
<div
ref={this.setInnerRef}
onTransitionEnd={() => this.updateAfterTransition()}
style={{
height: this.state.height,
overflow: "hidden",
transition: "height 250ms linear 0s",
}}
>
{children}
</div>
</div>
);
}
}
what i have tried so far.
index-functional.js
import React, { useEffect, useState } from "react";
import { usePrevious } from "./usePrevious";
const Collapse = (props) => {
const { title, children } = props || {};
const [state, setState] = useState({
showContent: false,
height: "0px",
myRef: null
});
const previousHeight = usePrevious(state.height);
useEffect(() => {
if (previousHeight === "auto" && state.height !== "auto") {
setTimeout(
() => setState((prevState) => ({ ...prevState, height: "0px" })),
1
);
}
}, [previousHeight, state.height]);
const setInnerRef = (ref) =>
setState((prevState) => ({ ...prevState, myRef: ref }));
const toggleOpenClose = () =>
setState((prevState) => ({
...prevState,
showContent: !state.showContent,
height: state.myRef.scrollHeight
}));
const updateAfterTransition = () => {
if (state.showContent) {
this.setState((prevState) => ({ ...prevState, height: "auto" }));
}
};
return (
<div>
<h2 onClick={toggleOpenClose}>{title}</h2>
<div
ref={setInnerRef}
onTransitionEnd={updateAfterTransition}
style={{
height: state.height,
overflow: "hidden",
transition: "height 250ms linear 0s"
}}
>
{children}
</div>
</div>
);
};
usePrevious.js - Link
import { useRef, useEffect } from "react";
function usePrevious(value) {
const ref = useRef();
useEffect(() => {
ref.current = value;
}, [value]);
return ref.current;
}
export { usePrevious };
The problem here is you set your reference to update through setState and useEffect (which is what causes you the infinite loop).
The way you would go by setting references on functional components would be as followed:
const Component = () => {
const ref = useRef(null)
return (
<div ref={ref} />
)
}
More info can be found here: https://reactjs.org/docs/refs-and-the-dom.html

How to render only 5 items in react autosuggest?

I'am using react autosuggest npm package to get the json data and display it. I want to display only 5 items. How to do it?
Form.js
import React from 'react'
import Autosuggest from 'react-autosuggest';
import cities from 'cities.json';
const getSuggestions = value => {
const inputValue = value.trim().toLowerCase();
const inputLength = inputValue.length;
// Here I get data from cities.json
return inputLength === 0 ? [] : cities.filter(lang =>
lang.name.toLowerCase().slice(0, inputLength) === inputValue
);
);
};
const getSuggestionValue = suggestion => suggestion.name;
const renderSuggestion = suggestion => (
<div>
{console.log('suggestion', suggestion)}
{suggestion.name}
</div>
);
class Form extends React.Component {
constructor() {
super();
this.state = {
value: '',
suggestions: []
};
}
onChange = (event, { newValue }) => {
this.setState({
value: newValue
});
};
onSuggestionsFetchRequested = ({ value }) => {
this.setState({
suggestions: getSuggestions(value)
});
};
onSuggestionsClearRequested = () => {
this.setState({
suggestions: []
});
};
render(){
const { value, suggestions } = this.state;
// Autosuggest will pass through all these props to the input.
const inputProps = {
placeholder: 'Search City...',
value,
onChange: this.onChange
};
return (
<div>
<Autosuggest
suggestions={suggestions}
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
getSuggestionValue={getSuggestionValue}
renderSuggestion={renderSuggestion}
inputProps={inputProps}
/>
<br/>
</div>
)
}
}
export default Form;
I want to render only 5 items, otherwise, computer hangs while loading huge data. Is there any other autocomplete react npm package, since I want only cities and country list. i.e when city is inputted, automatically the city name must be suggested with its relevant country.Any solution or suggestion highly appreciated. Thanks in advance
i modified you're getSuggestions() method a little i guess this should work for you.
const getSuggestions = value => {
const inputValue = value.trim().toLowerCase();
const inputLength = inputValue.length;
// Here I get data from cities.json
return inputLength === 0 ? [] : cities.filter(lang =>
lang.name.toLowerCase().slice(0, inputLength) === inputValue
).slice(0,5);
};
Use the Slice method with start index and last Index
suggestions={suggestions.slice(0, 5)}
import {
React
,Avatar
,axiosbase
} from '../../import-files';
import Autosuggest from 'react-autosuggest';
import './autosuggest.css';
import { withStyles } from '#material-ui/core/styles';
import TextField from '#material-ui/core/TextField';
import Paper from '#material-ui/core/Paper';
import MenuItem from '#material-ui/core/MenuItem';
let suggestions = [ { label: 'Afghanistan' } ];
function renderInputComponent(inputProps) {
const { classes, inputRef = () => {}, ref, ...other } = inputProps;
return (
<TextField
className={classes.textField}
fullWidth
variant="outlined"
InputProps={{
inputRef: node => {
ref(node);
inputRef(node);
},
classes: {
input: classes.input,
},
}}
{...other}
/>
);
}
function renderSuggestion(suggestion, { query, isHighlighted }) {
return (
<MenuItem selected={isHighlighted} component="div">
<div>
<strong key={String(suggestion.id)} style={{ fontWeight: 300 }}>
<span className="sugg-option">
<span className="icon-wrap">
<Avatar src={suggestion.Poster}></Avatar>
</span>
<span className="name">
{suggestion.Title}
</span>
</span>
</strong>
</div>
</MenuItem>
);
}
function initSuggestions(value) {
suggestions = value;
}
function getSuggestionValue(suggestion) {
return suggestion.Title;
}
function onSuggestionSelected(event, { suggestion, suggestionValue, suggestionIndex, sectionIndex, method }) {
console.log('HandleSuggestion() '+suggestionValue);
}
const styles = theme => ({
root: {
height: 50,
flexGrow: 1,
},
container: {
position: 'relative',
},
suggestionsContainerOpen: {
position: 'absolute',
zIndex: 998,
marginTop: theme.spacing.unit,
left: 0,
right: 0,
overflowY: 'scroll',
maxHeight:'376%'
},
suggestion: {
display: 'block',
},
suggestionsList: {
margin: 0,
padding: 0,
listStyleType: 'none',
},
divider: {
height: theme.spacing.unit * 2,
},
});
class IntegrationAutosuggest extends React.Component {
state = {
single: '',
popper: '',
suggestions: [],
};
componentDidMount() {
initSuggestions(suggestions);
}
// Filter logic
getSuggestions = async (value) => {
const inputValue = value.trim().toLowerCase();
var _filter = JSON.stringify({
filter : inputValue,
});
return await axiosbase.post(`${apiCall}`, _filter);
};
handleSuggestionsFetchRequested = ({ value }) => {
this.getSuggestions(value)
.then(data => {
if (data.Error) {
this.setState({
suggestions: []
});
} else {
const responseData = [];
data.data.itemsList.map((item, i) => {
let File = {
id: item.idEnc,
Title: item.englishFullName +' '+item.arabicFullName,
englishFullName: item.englishFullName,
arabicFullName: item.arabicFullName,
Poster: item.photoPath,
}
responseData.push(File);
});
this.setState({
suggestions: responseData
});
}
})
};
handleSuggestionsClearRequested = () => {
this.setState({
suggestions: [],
});
};
handleChange = name => (event, { newValue }) => {
this.setState({
[name]: newValue,
});
if(event.type=='click'){
if(typeof this.props.handleOrderUserFirstNameChange === "function"){
this.props.handleOrderUserFirstNameChange(newValue);
}
this.state.suggestions.filter(f=>f.Title===newValue).map((item, i) => {
//id
//Title
// Poster
if(typeof this.props.handleUserIDChange === "function"){
this.props.handleUserIDChange(item.id);
}
});
}
};
render() {
const { classes } = this.props;
// console.log('Re-render!!');
// console.log(this.props);
// console.log(this.state.suggestions);
const autosuggestProps = {
renderInputComponent,
suggestions: this.state.suggestions,
onSuggestionsFetchRequested: this.handleSuggestionsFetchRequested,
onSuggestionsClearRequested: this.handleSuggestionsClearRequested,
onSuggestionSelected: this.props.onSelect,
getSuggestionValue,
renderSuggestion,
};
return (
<div className={classes.root}>
<Autosuggest
{...autosuggestProps}
inputProps={{
classes,
placeholder: this.props.placeHolder,
value: this.state.single,
onChange: this.handleChange('single'),
}}
theme={{
container: classes.container,
suggestionsContainerOpen: classes.suggestionsContainerOpen,
suggestionsList: classes.suggestionsList,
suggestion: classes.suggestion,
}}
renderSuggestionsContainer={options => (
<Paper {...options.containerProps} square>
{options.children}
</Paper>
)}
/>
<div className={classes.divider} />
</div>
);
}
}
export default withStyles(styles)(IntegrationAutosuggest);

How to add right click menu to react table row, and access its properties?

I've added react-table package to my project and everything is fine, but I also wanted to have a possibility to right click on a row and perform some actions on it (cancel, pause etc). I'm using React with Typescript but I hope it doesn't add any complexity.
My initial idea was to use react-contextify, however I can't find any working examples that would combine react-table and react-contextify together.
The only "working" example I have found is this one:
React Context Menu on react table using react-contexify
I ended up not using react-contextify and it "kind of works" but I'm not totally certain about this one as I sometimes keep getting exceptions like this:
Uncaught TypeError: Cannot read property 'original' of undefined
The code I have now is this:
const columns = [
{
Header: "Name",
accessor: "name"
},
{
Header: "Age",
accessor: "age",
Cell: (props: { value: React.ReactNode }) => (
<span className="number">{props.value}</span>
)
},
{
id: "friendName", // Required because our accessor is not a string
Header: "Friend Name",
accessor: (d: { friend: { name: any } }) => d.friend.name // Custom value accessors!
},
{
Header: (props: any) => <span>Friend Age</span>, // Custom header components!
accessor: "friend.age"
}
];
return (
<div>
<ContextMenuTrigger id="menu_id">
<ReactTable
data={data}
columns={columns}
showPagination={false}
getTdProps={(
state: any,
rowInfo: any,
column: any,
instance: any
) => {
return {
onClick: (e: any, handleOriginal: any) => {
const activeItem = rowInfo.original;
console.log(activeItem);
},
onContextMenu: () => {
console.log("contextMenu", rowInfo);
this.setState({
showContextMenu: true,
rowClickedData: rowInfo.original
});
}
};
}}
/>
</ContextMenuTrigger>
{this.state.showContextMenu ? (
<MyAwesomeMenu clickedData={this.state.rowClickedData} />
) : null}
</div>
);
}
}
const MyAwesomeMenu = (props: { clickedData: any }) => (
<ContextMenu id="menu_id">
<MenuItem
data={props.clickedData}
onClick={(e, props) => onClick({ e, props })}
>
<div className="green">ContextMenu Item 1 - {props.clickedData.id}</div>
</MenuItem>
</ContextMenu>
);
const onClick = (props: {
e:
| React.TouchEvent<HTMLDivElement>
| React.MouseEvent<HTMLDivElement, MouseEvent>;
props: Object;
}) => console.log("-------------->", props);
What is the best (and simplest) way to add a context menu to react-table so I can use clicked row's props? I really like react-contextify but haven't found any examples.
Thanks
React Hooks exmaple on dev.to
Class Based Compnent example on codepen
class App extends React.Component {
constructor() {
super();
this.state = {
value: ''
};
}
render() {
return(
<div>
{
['row1', 'row2', 'row3'].map((row) => {
return (
<ContextMenu
key={row}
buttons={[
{ label: 'Editovat', onClick: (e) => alert(`Editace ${row}`) },
{ label: 'Smazat', onClick: (e) => alert(`Mažu ${row}`) }
]}
>
<div className="row">{row}</div>
</ContextMenu>
);
})
}
</div>
);
}
}
class ContextMenu extends React.Component {
static defaultProps = {
buttons: []
};
constructor() {
super();
this.state = {
open: false
};
}
componentDidMount() {
document.addEventListener('click', this.handleClickOutside);
document.addEventListener('contextmenu', this.handleRightClickOutside);
}
handleClickOutside = (e) => {
if (!this.state.open) {
return;
}
const root = ReactDOM.findDOMNode(this.div);
const context = ReactDOM.findDOMNode(this.context);
const isInRow = (!root.contains(e.target) || root.contains(e.target));
const isInContext = !context.contains(e.target);
if (isInRow && isInContext) {
this.setState({
open: false
});
}
}
handleRightClickOutside = (e) => {
if (!this.state.open) {
return;
}
const root = ReactDOM.findDOMNode(this.div);
const isInRow = !root.contains(e.target);
if (isInRow) {
this.setState({
open: false
});
}
}
handleRightClick = (e) => {
e.preventDefault();
console.log(e.nativeEvent, window.scrollY);
this.setState({
open: true,
top: window.scrollY + e.nativeEvent.clientY,
left: e.nativeEvent.clientX,
});
}
render() {
return (
<div
onContextMenu={this.handleRightClick}
ref={(node) => this.div = node}
>
{this.props.children}
{
!this.state.open
? null
: <div
className="context"
ref={(div) => this.context = div}
style={{ top: this.state.top, left: this.state.left }}
>
<ul>
{
// button - name, onClick, label
this.props.buttons.length > 0 &&
this.props.buttons.map((button) => {
return <li key={button.label}>
<a href="#" onClick={button.onClick}>
{button.label}
</a>
</li>
})
}
</ul>
</div>
}
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));

Reactjs Search after onChange

I have implemented a little search functionality in my reactjs application.
The Problem is, that my "searchHandler" function is triggered after every single letter the user enters in the textfield... So e.g. for the term "Lorem" my function is fetching 5 times from my api :(
How can I solve this problem?
Here is my code:
const { scaleDown } = transitions;
function searchingFor(term){
return function(x){
return x.title.toLowerCase().includes(term.toLowerCase()) ||
x.body.toLowerCase().includes(term.toLowerCase());
}
}
class ViewAll extends React.Component{
constructor(props){
super(props);
this.state = {
term: '',
mounted: true,
tracks: [],
hasMoreItems: true,
page: 2,
}
this.searchHandler = this.searchHandler.bind(this);
this.focus = this.focus.bind(this);
this.keyPress = this.keyPress.bind(this);
}
loadContent() {
var requestUrl = this.props.url;
fetch(requestUrl + this.state.page + '&_limit=3').then((response)=>{
return response.json();
}) .then((tracks)=>{
this.setState({ tracks: this.state.tracks.concat(tracks)});
this.setState({page: this.state.page + 1});
if(this.state.page === 6){
this.setState({hasMoreItems: false})
}
}).catch((err)=>{
console.log("There has been an error");
});
}
componentDidMount() {
window.scrollTo(0, 0);
var requestUrl = this.props.url;
fetch(requestUrl + '1&_limit=3')
.then((response)=>{
return response.json();
}) .then((data)=>{
this.setState({tracks : data});
})
.catch((err)=>{
console.log("There has been an error");
});
//this.focus();
}
searchHandler(event){
this.setState({term: event.target.value});
var requestUrl = 'https://questdb.herokuapp.com/all?q='
fetch(requestUrl + this.state.term).then((response)=>{
return response.json();
}) .then((tracks)=>{
this.setState({ tracks: this.state.tracks.concat(tracks)});
}).catch((err)=>{
console.log("There has been an error");
});
}
focus() {
this.textInput.focus();
}
keyPress(e){
if(e.keyCode == 13){
console.log('value', e.target.value);
// put the login here
}
}
render() {
const {term, data, tracks} = this.state;
const loader = <div className="loader2"> </div>;
var items = [];
const imageUrl = require(`../assets/Book.jpg`)
tracks.filter(searchingFor(term)).map(function(title, i)
{
items.push(
<div>
<MuiThemeProvider>
<Paper style={{ borderRadius: "2em",
background: '#ffffff'
}} zDepth={1} >
<ItemViewAll
key={title.id}
/>
</Paper>
</MuiThemeProvider>
</div>
);
}, this);
return (
<div>
<Fade in={true} timeout={1000}>
<div >
<MuiThemeProvider>
<TextField hintText='Bot suchen...'
type="Text"
onChange={this.searchHandler}
value={term}
underlineFocusStyle={{borderColor: '#B00020', borderWidth: 3}}
underlineStyle={{borderColor: '#B00020', borderWidth: 1.5, top: '45px'}}
hintStyle={{fontSize: '8.1vw', fontFamily: 'Anton', color: 'rgba(255,255,255,0.9)'}}
inputStyle={{fontSize: '8.1vw', fontFamily: 'Anton', color: '#ffffff'}}
ref={(input) => { this.textInput = input; }}
style={{caretColor: '#ffffff', width: '90%', maginLeft: 'auto', marginRight: 'auto', marginTop: '12%' }}
InputLabelProps={{ shrink: true }}
/>
</MuiThemeProvider>
</div>
</Fade>
<InfiniteScroll
pageStart={1}
loadMore={this.loadContent.bind(this)}
hasMore={this.state.hasMoreItems}
initialLoad={true}
>
{items}
</InfiniteScroll>
</div>
)
}
}
export default ViewAll;
Here you can check out the Website with the broken search function. As you can see the items are shown double or even triple... After the textfield is emptied, the search results should be removed and only the normal fetched ones should be shown.
https://www.genko.de (use the mobile version in chrome)
Thank you :)
Use lodash debounce. It is used for this exact use case
https://stackoverflow.com/questions/48046061/using-lodash-debounce-in-react-to-prevent-requesting-data-as-long-as-the-user-is
Sample:
import React, {Component} from 'react'
import { debounce } from 'lodash'
class TableSearch extends Component {
//********************************************/
constructor(props){
super(props)
this.state = {
value: props.value
}
this.changeSearch = debounce(this.props.changeSearch, 250)
}
//********************************************/
handleChange = (e) => {
const val = e.target.value
this.setState({ value: val }, () => {
this.changeSearch(val)
})
}
//********************************************/
render() {
return (
<input
onChange = {this.handleChange}
value = {this.props.value}
/>
)
}
//********************************************/
}
If you don't need full lodash package you can write it yourself:
function debounce(f, ms) {
let timer = null;
return function (...args) {
const onComplete = () => {
f.apply(this, args);
timer = null;
}
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(onComplete, ms);
};
}
The first arg (f) is your function which should not be performed more often than
second arg (ms) - amount of ms ). So in your case you can write your handler in next way:
handleChange = debounce((e) => {
const val = e.target.value
this.setState({ value: val }, () => {
this.changeSearch(val)
})
}, 1000) // second arg (1000) is your amount of ms

React passing data from child component to parent component

I am using a material UI Auto-suggest component and i would like to pass the full name to the parent component. This link is similar to my code https://codesandbox.io/s/ryn76v485m
The parent component is passing down the emailUser props
<SearchForUsers emailUser={this.emailUsers}/>
emailUsers = (user) => {
debugger
console.log(user + "trying to pass down from child")
}
The problem that i am having is that i cannot get the child component to pass the state correctly to the parent component.
At the moment i am doing the this.props.emailUser(this.state.values) after the mapping of the employees. The state is only change after the second person has been entered. I tried putting the this.props.emailUser into the onChange but that event does not update the state when the user clicks on the suggested name. Can anyone tell me how to do get the state back to the parent component correctly.
This is my child component.
class ShareForUsers extends Component {
constructor(props){
super(props);
this.state = {
menuOpen: false,
value: "",
values: []
};
}
componentDidMount() {
if (!!this.props.employees && this.props.employees.length == 0) {
this.props.listEmployees();
}
}
componentWillReceiveProps(nextProps) {
this.setState({ ...nextProps })
}
render() {
return (
<div>
<TextField
fullWidth
value={this.state.value}
InputProps={{
startAdornment: this.state.values
.concat()
.sort(({ label: aLabel }, { label: bLabel }) => {
if (aLabel < bLabel) return -1;
else if (aLabel > bLabel) return 1;
return 0;
})
.map(chip => (
<InputAdornment
component={Chip}
label={chip}
onDelete={() => {
const value = chip;
this.setState(({ values: prevValues }) => {
const values = prevValues;
const idx = values.indexOf(value);
if (idx === -1) {
values.push(value);
} else {
values.splice(idx, 1);
}
return {
values
};
});
}}
/>
))
}}
onChange={evt => {
const value = evt.target.value;
this.setState({
value,
menuOpen: value.length > 0
});
}}
onFocus={() =>
this.setState(({ value }) => ({
menuOpen: value.length > 0
}))
}
onBlur={() => this.setState({})}
/>
<div>
{this.state.menuOpen ? (
<Paper
style={{
position: "absolute",
zIndex: 100,
width: "100%"
}}
>
{this.props.employees
.filter(
employee =>
employee.user.email.toLowerCase().indexOf(this.state.value) > -1
)
.map(employee => (
<MenuItem
key={employee.user.id}
onClick={() => {
this.setState(({ values: prevValues }) => {
const values = prevValues.concat();
const idx = values.indexOf(employee.user.id);
if (idx === -1) {
values.push(employee.user.email);
} else {
values.splice(idx, 1);
}
return {
values,
value: "",
menuOpen: false
};
});
}}
>
{employee.user.email}
</MenuItem>
))}
</Paper>
) : (
""
)}
</div>
</div>
)
}
}
const shareForUsers = withStyles(styles)(ShareForUsers)
export default connect(
state => state.user,
dispatch => bindActionCreators(actionCreators, dispatch)
)(shareForUsers);
Thanks
In onChange event you can pass the value from SearchForUsers component to its parent by this:
onChange={evt => {
const value = evt.target.value;
this.setState({
value,
menuOpen: value.length > 0
});
this.props.emailUser(value);
}}

Resources