Office ui Fabric People Picker, pass varaibles to onChangeEvent - reactjs

I would like to know if there is a possibility to added a variable to the default onchange event of the office ui fabric react People Picker component.
In the onchange event default is onChange?: (items?: IPersonaProps[]) => void
I would like to pass an variable to add to an array like Key Value for using later in my code.

You can build a HOC to achieve this.
Define
// Your HOC component
interface Props {
yourCustomState: string // get custom value from props
...
return (
<>
<Select
onChange={(e: any) => onChange(e, yourCustomState)} // add custom value to callBack function
...
Usage
handleOnChangeEvent = () => (event, yourCustomStateValue: string) => {
console.log(yourCustomStateValue);
... // event
}
<YourComponent
yourCustomState={value}
onChange={this.handleOnChangeEvent()}
...

Related

React/Typescript - How to handle different event types in single onChange handler function

I have a form which is built up of differing types of inputs, utilising the mui library. I want to take the values of differing types of input components, and store them within a single state in a Grandparent component. I am able to handle different types of inputs if they have the same Event function structure (i.e. (e: React.ChangeEvent<HTMLInputElement>) => void), but I am now using the AutoComplete component, and since it has a different onChange event handler, I am unsure as to how I would be able to process it's change within a single onChange handler.
Please look at this codesandbox link for the rough structure of the problem. I would like the input of Child3 to be handled within the getNameAndValueFromEvent() method within valueStates.ts (which is called in Grandparent.tsx).
Autocomplete's onChange event is slightly different than TextField's onChange event. As a result, you cannot directly handle them by a single value change handler function.
You can add a new value change handler for Autocomplete in Child3 component to send the required parameters for valueChange function.
Your Child3 component can be like:
const Child3: React.FC<Props> = ({ id, valueChange }) => {
const handleValueChange = (event: React.SyntheticEvent, val: string) => {
const dataToBeSent = {
target: {
name: id,
value: val
}
}
valueChange(dataToBeSent, id)
}
return (
<>
<Autocomplete
multiple
disableCloseOnSelect
id="tags-outlined"
options={options}
getOptionLabel={(option) => option.name}
filterSelectedOptions
onChange={handleValueChange}
renderInput={(params) => (
<TextField
{...params}
label="Child3"
placeholder="Option"
name={id}
/>
)}
/>
</>
)
}
You can take a look at this forked sandbox for a live working example of this approach.

MGT PeoplePicker selectionChanged event parameter type

I am creating a SPFx web part with React and I am using the Microsoft Graph Toolkit PeoplePicker. It offers a selectionChanged callback. This callback gets passed a parameter of type Event (defined in typescript/lib/lib.dom.d.ts)
export type PeoplePickerProps = {
// ....
selectionChanged?: (e: Event) => void;
}
However, according to this documentation I need to access event.target.selectedPeople. But selectedPeople does not exist as property of event.target.
My code looks like this:
import * as React from 'react';
import { PeoplePicker, People } from '#microsoft/mgt-react/dist/es6/spfx';
export const EmployeePicker: React.FC = (): React.ReactElement => {
const [people, setPeople] = React.useState([]);
const handleSelectionChanged = (event: Event): void => {
console.log('Selection changed');
console.log('event', event);
setPeople(event.target.selectedPeople);
};
return (
<div>
<PeoplePicker selectionChanged={handleSelectionChanged} />
Selected People:
<People people={people} />
</div>
);
};
I'd like to avoid opting out of the type system. (I guess changing the type of event to any would work) And rather know how I can access the event.target.selectedPeople prop using typescripts type system.
You can use event.details too. You can read about it here https://learn.microsoft.com/en-us/graph/toolkit/customize-components/events. For the people picker there will be an array of the currently selected people.
To make it work with typescript you can either use any or do something like event.target["selectedPeople"] or event["details"]. Or you can create a new type like
type PeoplePickerEventTarget = EventTarget & {selectedPeople: IDynamicPerson[]};
I am not sure what the type of the array is, you can replace any with the correct one or just build your own.
And then in the code you can do something like this
<PeoplePicker
selectionChanged={(e: Event) => {
const target = e.target as PeoplePickerEventTarget;
//do something here
}}
></PeoplePicker>

React Hooks SetState Method isn't updating the state at all

I am using a Material UI Select Component to render a simple drop down menu, with its value as a state declares using the useState method.
const [collaboratingTeams, setCollaboratingTeams] = useState([])
The below code is of the Select Component, with its value and the corresponsing handler function in its onChange prop.
<Select
validators={["required"]}
errorMessages={["this field is required"]}
select
multiple
variant="outlined"
value={collaboratingTeams}
name="collaboratingTeams"
onChange={(e) => handleSelectCollaboratingTeams(e)}
helperText="Select Collaborating Teams "
>
{arrTeams.map((option, index) => (
<MenuItem
key={option.teamId}
value={option.teamId}
variant="outlined"
>
<Checkbox
checked={collaboratingTeams.indexOf(option.teamId) !== -1}
/>
<ListItemText primary={option.teamValue} />
</MenuItem>
))}
</Select>
The below code is the function that triggers when a drop down data is changed.
This function sets the state, which should then technically update the Select's selected options.
const handleSelectCollaboratingTeams =(e)=>{
setCollaboratingTeams(e.target.value)
}
The issue is, the setCollaboratingTeams method isn't updating the state only. I understand that the setstate method in hooks works so coz of its asynchronous nature but at some point it should display up right. Don't understand where I'm going wrong.
I expect the collaboratingTeams array to be updated with a new value when a new value is selected by the user.
you should define the new state for storing the selected item.
Example for class component:
state = {
selectedOption: null,
};
handleChange = selectedOption => {
this.setState({ selectedOption });
};
Example for functional component(using React-hook):
const [selectedOption, setSelectedOption] = useState(null);
handleChange = selectedOption => {
setSelectedOption(selectedOption);
};
dont use arrow function with onchange it often used when we need to pass id or some other data

How to update an array using setState with React Hooks on Strict Mode

I have this, every time addEmail is called, the updater callback for setEmails is called twice and therefore the new email is added twice:
const Container = (props: Props) => {
const [emails, setEmails] = useState<Array<string>>([]);
const addEmail = (email: string) => {
setEmails((prevState: string[]) => [...prevState, email]);
};
return (
<Wrapper>
{emails.map((email, index) => {
return (
<Email
key={index}
text={email}
onRemoveClicked={() => {}}
/>
);
})}
<Input onSubmit={addEmail} />
</Wrapper>
);
};
How am i supposed to do this on strict mode ?
You need to provide a lot more context here, This component doesn't look like a native html form. Email and Input both are custom components.
I would suggest you to try the below steps to debug this
Check if Input component is calling onSubmit twice.
React's useState setter method takes the new state as argument, you are passing a function here, that might be the problem. Try changing the addEmail function definition to below and see if it works
const addEmail = (newEmail: string) => setEmails([...emails, newEmail]);
Read More about React state hook.

Can't get the target attributes of material-ui select react component

I'm trying to get the id,name, and value from Select field element of Material-UI react component.
This is my container:
//some code is removed to keep things simple
class MyContainer extends Component {
constructor(props) {
super(props);
}
render() {
return(
<MyComp onChange={this._onChange.bind(this)} />
);
}
_onChange(evt) {
console.log(evt.target);
console.log(evt.target.id); //blank
console.log(evt.target.name); //undefined
console.log(evt.target.value); //html value of selected option!
}
}
export default connect(select)(MyContainer);
in my presentational component:
import React, {Component} from 'react';
import Select from 'material-ui/SelectField';
import MenuItem from 'material-ui/MenuItem';
const someData = ["aaaa", "bbbb", "ccccc"];
class MyComp extends Component {
render() {
const {onChange} = this.props;
return (
<form >
<Select
value={this.props.test}
name={"test"}
id={"test"}
onChange={this.props.onChange}
hintText={"Select a fitch rating service"}>
{
someData.map((e) => {
return <MenuItem key={Math.random()} value={e} primaryText={e}/>
})
}
</Select>
</form>
);
}
}
Problem is that _onChange(evt) is giving this values:
evt.id is blank
evt.name is undefined
evt.target.value is <div>whatever the selected value was</div>
It seems as though the passed argument to _onChange(evt) is not the SELECT but rather the option since when i print out evt.target it gives <div>whatever the selected value was</div>. anyone knows why? If i use a plain select field (not material-ui) then this works as expected (i.e. i can get the id,name, correct value of selected option). How do i get the target id, name..etc from onChange event of Material-UI Select component?
P.S i'm using the same _onChange method for TextField component of material-ui and it works there. I've also tried:
_onChange = (event, index, value) => {
console.log(event.target.id); //blank
console.log(event.target.name); //undefined
console.log(index); //correct index
console.log(value); //correct value
};
I'd keep it simpler using event.currentTarget instead of event.target.
In your event handler, you can access the attribute of interest from the element that triggered the event by:
_onChange(evt) {
console.log(evt.currentTarget.getAttribute("data-name");
console.log(evt.currentTarget.getAttribute("data-value");
}
thus calling .getAttribute on the attribute name prepended by "data-"
Update 2
In response to your comments:
As per the material-ui docs, getting back the touchtap event on option element rather than the select element is expected. If you want the id and name of the element, I would suggest binding the variables to the callback:
The onchange method in the parent component:
_onChange(id, name, evt, key, payload) {
console.log(id); //id of select
console.log(name); //name of name
console.log(payload); //value of selected option
}
And when you attach it to the select component, you need to use bind
<Select
value={this.props.test}
name={"test"}
id={"test"}
onChange={this.props.onChange.bind(null,"id","name")}
hintText={"Select a fitch rating service"}>
Update
Here are the react event docs. Under the event-pooling you will find reference to the use of e.persists() in the block quote. The explanation given in this issue is that React pools the event object, so it get's reused when another event is fired.
React has a rather special event object. It wraps the native event in a synthetic event, which is what event points to in your _onChange method.
The problem is that this synthetic event object is recycled and reused for the next event, so it is garbage collected and set to null. I don't think this is well documented, but I'll search for a link and update this answer once I find it.
The solution is for the first line in your event handler to make a copy of the event, persist it, or get a reference to the native event:
Make a copy of the event
_onChange = (event, index, value) => {
e = _.cloneDeep(event); // Here I'm using the cloneDeep method provided by underscore / lodash . User whatever library you prefer.
console.log(e.target.id);
console.log(e.target.name);
};
Persist the event
_onChange = (event, index, value) => {
event.persist() // This stops react from garbage collecting the event object. It may impact performance, but I doubt by much.
console.log(event.target.id);
console.log(event.target.name);
};
Get a reference to the native event object
_onChange = (event, index, value) => {
e = event.native; // This looks the same as a vanilla JS event
console.log(e.target.id);
console.log(e.target.name);
};
This is an answer for latest version of Material UI and React (2020):
You need to have the name property on your <Select> element. That name value should correspond to the field's name in your object.
This should work (name is set to the field you're trying to update):
<Select
labelId="demo-simple-select-outlined-label"
id="demo-simple-select-outlined"
name="taskType"
value={props.job.taskType || ""}
label="{props.label}"
onChange={props.onTaskTypeChange}
>
But this will not work (name is omitted). This will return evt.target.name = undefined:
<Select
labelId="demo-simple-select-outlined-label"
id="demo-simple-select-outlined"
value={props.job.taskType || ""}
label="{props.label}"
onChange={props.onTaskTypeChange}
>

Resources