Office UI Fabric React - Dropdown not respecting selectedKey prop - reactjs

I have this code:
<Dropdown
selectedKey={someKeyInState}
onChange={(e,option) => {
// check if the dropdown should be updated
if(someCondition){
// update selected key
}
else {
// don't update selected key
}
}}
options={someOptions}
/>
I want to block updating the selected key if a certain condition is met.
But, Dropdown visually shows the option that I clicked on as selected.
How do I prevent this behavior?

If you are using react, you should not do in this way. Use a global state optionState and try to use conditional checks which state should be shown to use onChange method is triggered. In order to do this. First extract your conditional check to separate function.
myFunction = () => {
// do conditional check and setState for optionState
}
--------------------------
onChange={myFunction}
Then when you select pop the key and value from the option state and update to the selectedState, now react automatically update the state and UI

The problem was my state for selectedKey was undefined it nothing is selected. When you pass in undefined you're basically saying to fabric to control the selectedKey state on it's own. I fixed by passing null instead of undefined. That made the Dropdown fully controlled.

Related

How to properly display a Material UI Native select and change the selection using a state hook

Code SandBox Here
https://codesandbox.io/s/wild-brook-jpeio?file=/src/App.js
I acquire an array of values using useEffect hook. The array variable is set using useState hook
The value and onChange attributes of Native Select also uses another state variable and its set function to update it.
Using the below part of code to use the set function to set the selected value of the Native Select just after the useEffect gets the data.
// set the default selection on first load
if (!isEmpty(combolist)) {
console.log(combolist[0].name);
setSelection(combolist[0].name);
}
But this is giving too many renders error.
How to fix this and achieve my objective
One easy way is move the set the combolist in useEffect to prevent the multiple re-render.
This is the solution link.
you need to use a useEffect hook for that like this:
useEffect(() => {
if (data.length) {
const combolist = data.map((obj) => pick(obj, ["id", "name"]));
// set the default selection on first load
if (!isEmpty(combolist)) {
console.log(combolist[0].name);
setSelection(combolist[0].name);
}
}
}, [data]);

How to reload a specific tag in reactJS

I want to reload the UserPlaylist tag after running the onChangeTracks() function in order to update it's contents but I'm not sure how to re-execute a specific tag if possible.
Parent Component:
render(){
return(
<li><UserPlaylist onChange={this.onChangeTracks}/></li>
);
}
UserPlaylist Component (Child Component):
render() {
return(
window.addEventListener('DOMContentLoaded', (event) => {
this.getPlaylists() //Have tracks load immediately
}),
<select value={"DEFAULT"} onChange={this.props.onChange}>
<option value="DEFAULT"> Add to Playlist </option>
<option value="new"> New Playlist </option>
{
this.state.users_playlists.map((playlist, index) => (
<option key={index} value={playlist.id}> { playlist.name }</option>
))
}
</select>
);
}
Components will rerender every time their state is updated. So you should update the state of your Parent component whenever the this.onChangeTracks function is invoked. Since this.state.currentTrack is being passed in as a prop to UserPlaylist, once it is updated via a call to setState the UserPlaylist component will receive new props and should rerender those new props accordingly.
Example:
onChangeTracks(val) {
...
this.setState({currentTrack: val});
...
}
EDIT
Here's a Codesandbox that updates the "New Playlist" select element whenever the onChange event is called.
Here's how it works: First, the useEffect hook fires and loads in data from the examplePlaylists variable. In your app, you should populate this with the data you have saved in your database or localStorage, etc. This data is passed to the playlists state which uses the useState hook. Whenever a person selects an option from the dropdown menu, the onChange event is fired and calls the handleUpdatePlaylists function. This function first prompts the user to enter the name of their new playlist. It then updates the playlists state through the updatePlaylistsfunction. Now, since the state has changed, the component will rerender and map over all the playlists, displaying their names in the dropdown menu. All without needing to refresh the page.
Of course, since I don't have a DB to save these to, the new playlists will disappear on page refresh. But you can write logic to save them however you wish.
And while this works, I think that you should look to using buttons and the onClick event instead of a select element to handle this logic. Since a new playlist is created whenever an option is selected, then clicking on the name of any playlist will also create them. This doesn't appear to be intended functionality.
So to summarize: handle updates to your components by changing their state. Once state changes, the components will rerender with their new state available for you to work with.

Checkbox checked status

I'm using MaterialUI components and i map through an array of object to generate some checkboxes as shown below.
const wrapper = () => {
...
return(
<FormGroup>
{Object.keys(products).map((key) => {
return <FormControlLabel label={products[key].name} key={key} control={
<CheckBox value={products[key].name} />
}
})
);
}
So given the code above. Let's say the products array has 3 object. Whenever i check a checkbox, i want all others to get a checked false and the one i checked a check true.
I'm using the state Hook so the code above is a functional component.
Material-UI Checkbox Component takes a prop checked of boolean type.
What you can do is maintain the flag for each checkbox and whenever any checkbox if pressed, just make its value in state true, and false the other and pass that state checked prop of each checkbox.
BTW, the behavior you want to achieve here is not done by checkboxes, it is the behavior of Radio Button, where the user can select only one option at a time while checkboxes are used for multiple selections.
(I believe material-ui checkbox documentation has a very clear explanation on this, Let me know if you can keep up by yourself or you want sample code too.)
I would suggest using Radio buttons instead of checkBoxes in this case.
https://material-ui.com/components/radio-buttons/
No more than one radio button can be selected at a time which is what you are describing.

Manually update DraftJs ContentState with clicked text

How can I manually update DraftJs's ContentState in response to clicked text?
I have a list of text item. When one is clicked I am passing that text down to Draftjs, but because I am setting the state using componentWillReceiveProps() it requires that I click the text twice to get an update.
componentWillReceiveProps() {
const activeNoteText = this.props.activeNoteText;
if (activeNoteText !== '') {
this.setState({ editorState: EditorState.createWithContent(ContentState.createFromText(activeNoteText)) });
}
}
First click: Update the App state and pass props down to Draftjs (component updates before receiving new props)
Second click: Now the prop is properly set and Draftjs updates (component updates with the props received on the first click)
How can I accomplish this in one pass? I know there's no componentDidReceiveProps and I know there's a good reason, though I can't claim to fully understand yet, so what's the best practices way to accomplish something like this?
Why are you using componentwillReceiveProps?.
What you can do, is have the states your setting in Draftjs i.e. editorState(Well, that's what I can make out) in its parent and whenever a list item is clicked on the click handler for that update the editorState and then pass it as props to Draft js.
Further for the condition, where you are checking if it is not empty,
You could use the
getInitialState(){
.....
}
For initialization when your component is initially loaded. So you could have a default value for editorState.

React rerender only one child

in my form i have a few dropdown components. Whenever first dropdown option changes i want to update props for the second dropdown and rerender it. My code looks like this
handleProjectChange(option) {
//this.setState({ selectedProject: option })
this.refs.phase.props = option.phases;
//this.refs.forceUpdate()
this.refs.phase.render()
}
render() {
var projectOptions = this.projectOptions
var defaultProjectOption = this.state.selectedProject
var phaseOptions = defaultProjectOption.phaseOptions
var defaultPhaseOption = phaseOptions[0]
var workTypeOptions = api.workTypes().map(x => { return { value: x, label: x } })
var defaultWorkTypeOption = workTypeOptions[0]
return (
<div>
<Dropdown ref='project' options={projectOptions} value={defaultProjectOption} onChange={this.handleProjectChange.bind(this)} />
<Dropdown ref='phase' options={phaseOptions} value={defaultPhaseOption} />
<Dropdown options={workTypeOptions} value={defaultWorkTypeOption} />
<button className="btn btn-primary" onClick={this.handleAddClick.bind(this)}>Add</button>
</div>
)
}
But props are not changed, so it rerenders the same options. At the moment im just rerendering entire form by setting new state on it. Is there any way to rerender only one child/Dropdown with new props?
The way to do this is to put the selected option in first dropdown selectedProject in state.
And inside your render function, fetch/ populate the options in the second dropdown, dependent on the selected project.
Flow will then be:
User selects an option in the first dropdown.
This triggers handleProjectChange()
Inside handleProjectChange(), the newly selected option is put in state, by a this.setState() call
Because state changed, react re-runs the entire render() function.
Under the hood, react figures out that only the second dropdown has changed, so react will only re-render the second drop-down on your screen/ in the DOM.
Although React does have a reconciliation algorithm that dynamically checks whether each component should be rerenader or not in every rendering of its parent, it doesn't always work as we intended.
https://reactjs.org/docs/reconciliation.html
For this kind of issues, you have two options. You can use either React.pureComponent or React.useMemo().

Resources