Checkbox not changing check attribute after state was changed - reactjs

Here is a some parts of code I am using:
class FileItem extends Component {
constructor(props) {
super(props);
// Initial state
this.state = {
hover: false,
checked: false
};
this.pickFile = this.pickFile.bind(this);
}
pickFile(e){
e.stopPropagation();
this.state.checked ? this.props.removeSelectedPaths(this.props.path) : this.props.addSelectedPaths(this.props.path);
}
//set the checked status for the checkboxes if file\folder are picked
componentWillReceiveProps(nextProps){
this.setState({checked: nextProps.selectedPaths.indexOf(this.props.path) != -1 });
}
render(){
return(
<div className='select-mode'><input id='files-select' type='checkbox' checked={this.state.checked} onClick={this.pickFile}/></div>
)
}
How this should work:
When you pick a file it should set the checked status of its checkbox. And when you use slect all files checkbox( it is not in code here) it pushes all paths to the redux state in the selectedPAths array and all files should have checked checkboxes as they are selected.
Components structure:
Files - container conencted to the redux state. Send props to FileList
FileList - dumb component wrapper for each FileItem. Sending props to FileItem.
FileItem - component which renders row for specific file
The problem:
The problem is when I use Select All files functionality the checkbox for individual file not updating the UI to show the check mark on checkbox. While picking file by file is working fine and checkbox changes to checked(but same logic is used to see if it should be checked).
Weird facts:
When I do
console.log(this.state.checked) in render it shows me that state is
true. So it should change checkbox to checked but not.
I have a similar logic for the Select All files checkbox ( it uses componentWillReceiveProps method to check if all files was selected and change the checked status) and it is working just fine.
You can reveal that the checkbox checked after you picked all files just hovering it over(which triggers on mouse enter event and changes the state which leads to re rendering). I have a feeling like a render is getting lost after state changed but the weird fact 1 tells opposite thing as we have true in state but not checked checkbox in UI.

Sometimes the component needs a key, usually when using lists. this is useful for detecting changes. Try this:
<FileItem key={file.index.toString() + file.isChecked} path={filepath} checked={file.isChecked} />
We say to react that there was a change. To re-render.
Suggest:
You must pass the checked parameter from the parent if you want to handle the status of all the items
files.map (file =>
<fileItem key={file.key} path={filepath} checked={AllChecked} />
and at FileItem:
class FileItem extends Component {
constructor(props) {
super(props);
// Initial state
this.state = {
hover: false,
checked: props.checked// <= here
};
this.pickFile = this.pickFile.bind(this);
}

Related

React nested state object not updating

I am trying to update a nested state object (checkedObjects) in a react class component, to track when checkboxes are checked and unchecked. checkedObjects has the following structure:
checkedObjects: {
[assignmentName]: boolean,
}
verifyObjects is a local variable that checks if the new name property was actually received. When I console out the contents of these objects however, checkedObjects is empty, while the new property was added to verifyObjects (see screenshot below). Can anyone advise why the state variable checkedObjects is not updating immediately?
Screenshot:
Code Snippet:
this.state = {
checkedObjects: {},
};
incrementCount(totalCount, id, checked, assignmentName) {
console.log("increment: totalCount", totalCount, " ; id:", id, checked);
// If a checkbox is clicked with an assignment name store on the checkedObjects
if (assignmentName) {
let verifyObjects = { ...this.state.checkedObjects };
verifyObjects[assignmentName] = checked;
this.setState(prevState => {
let tempObj = {...prevState.checkedObjects}
tempObj[assignmentName] = checked;
return {checkedObjects: tempObj}
});
console.log("SelectedAssignmentsObj:", this.state.checkedObjects);
console.log("VerifiedObject:", verifyObjects);
} //if
}
State updates don't occur instantaneously. When we call the setState() function, it schedules a state update. Try console logging tempObj to see the value that is being put inside of this.state.checkedObjects.
In short, your code is working the way it should but you wont be able to see the state update right after calling this.setState() [because the state update is scheduled and didnt happen at that instant]. If you want to ensure that your state did update the way you wanted, can add a dummy button on the side that console logs the value of this.state.checkedObjects or you can use the chrome extension React Developer Tools to find out the values in the state object.

MultiSelect does not update value when value state changes (PrimeReact UI)

https://www.primefaces.org/primereact/showcase/#/datatable
https://www.primefaces.org/primereact/showcase/#/multiselect
I am using the PrimeReact library to create and customize a Data Table.
My table is dynamic and will build itself based on the data given to it. I am assigning different filters to each column depending on the column's data type, but because there are a variable number of columns I must create the filters dynamically.
To accomplish this I am factoring out the filter logic into a separate class which contain their state and logic.
My issue is that the MultiSelect component I am using as a filter interface does not update its value when it's value's state is updated. After updating the state the value remains null. As the MultiSelect component does not have a reference to the previously selected values I can only choose one value at a time.
I think I am missing some understanding regarding class components, as I usually use functional components. I used a class component in this case so that I could access filterElement from the instantiated DropDownFilter class through DropDownFilter.filterElement() and use as a prop in the Column component.
import React from 'react'
import { MultiSelect } from 'primereact/multiselect';
class DropDownFilter extends React.Component {
constructor(props) {
super(props);
this.multiSelRef = React.createRef();
this.state = {
selectedOptions: [],
}
// Added following two lines trying to fix issue but they did not alter behaviour
this.onOptionsChange = this.onOptionsChange.bind(this)
this.filterElement = this.filterElement.bind(this)
}
onOptionsChange = (e) => {
this.props.dt.current.filter(e.value, this.props.field, 'in');
this.setState({selectedOptions : e.value})
}
filterElement = () => {
return (
<React.Fragment>
<MultiSelect
ref={this.multiSelRef}
value={this.state.selectedOptions} //*** value is null even when selectedOptions has been updated after user chooses option.
// Confirmed by viewing value through multiSelRef
options={this.props.options}
onChange={this.onOptionsChange}
optionLabel="option"
optionValue="option"
className="p-column-filter"
/>
</React.Fragment>
)
}
}
export default DropDownFilter;
I learned the state was not working in this case because I was instantiating the DropDownFilter using the new keyword in when using it. This means it wasnt being mounted to the DOM and could not use state.
I am still having issues regarding implementing the custom columns with filters, but I have posted a new question to handle that scope with my new findings.

With ReactJS Popup, how can I make it only show one pop up at a time?

I am experimenting with different React popup libraries. I found reactjs-popup and began playing with the codesandbox. I created a fork of the environment here https://codesandbox.io/s/pp60zjkxlj
When I click the first (or second) button, it displays the corresponding popup. What i'd like is so that when I click other button, it will display that button's popup and hide any other popups that are visible. The goal is that only one popup should be visible on screen at any time.
Is this possible with this library?
See https://codesandbox.io/s/pp60zjkxlj
have a common state for popup when first button is clicked set the state to first button and based on the state
I would look into Portals where you can use a portal that sits outside of the jsx that wishes to use it.
imo it is the best way to implement a modal and i believe there's not to much need to add another dependency to your project and just create one this way that matches your needs.
You will need to pass props into the Popup to tell it when to open and when not to open.
Your component should have state set for each popover or be passed in props to tell the Popovers when to display. You can set in a constructor a default state and spread those before setting the new state with the new Popover open. This is how I handle modals.
constructor(props) {
super(props);
this.state = {
vendor: {} // Just showing my default state this gets set and this.defaultState is spread after my fetch request completes in componentWillMount()
};
this.defaultState = {
isPopoverOpen: {
// ---- Default Popover booleans can go here
editVendor: false
}
};
}
Actual function that controls the Popover status in the state.
// onClick of element would be onClick={this.handleTogglePopover('editVendor')}
handleTogglePopover = id => {
const currentState = Object.assign({}, this.state);
this.setState({
currentState,
...this.defaultState, // spreading default will set everything to false
isPopoverOpen: {
[id]: true // then the newly clicked to true
}
});
};

How to store the information in react component.

When I am asking this question, lots of doubts are coming into my mind. well, first I will give my problem description.
I have component X. and it contains checkboxes and a search box.
while something typed (call it search_query) in search box,
X needed to update the checkboxes which matches the search_query. [note that I got all the values of checkboxes by some api call. and it is done when component created. ]
First doubts I came to my mind is that
store (search_query) and (values of checkboxes) in component state
if the values are more searching takes more time.
is it possible to change the values of props inside the component
or is there any other way to do it ??
Since no code is shared. Assuming you are using plain React ( no Redux, middlewares are used ).
Answering your question:
[1] Is it possible to change the values of props inside the component?
Changing props values inside the component is a bad approach.
"All React components must act like pure functions with respect to their props."
[ref: https://facebook.github.io/react/docs/components-and-props.html#props-are-read-only]
Also, the view doesn't get the update if props values changed within the component.
[2] or is there any other way to do it.
yes ( without mutation inside the component )
using "state" property to hold values & setState to update values.
[3] How to store the information in react component?
Let's rename component X as FilterData,
searchbox ( SearchBox ) & checkboxes (SelectionBox) are two individual components.
// Define state to FilterData
class FilterData extends React.Component {
constructor() {
super()
this.state = {
term: '',
searchResults: []
}
}
....
}
// pass the state values to child components as props
class FilterData extends React.Component {
....
render() {
return (
<div>
<SearchBox term={this.state.term} />
<SelectionBox options={this.state.searchResults} />
</div>
)
}
}
In React App,
data flows top down (unidirectional) and there should be a single source of truth.
SearchBox & SelectionBox are two individual (sibling) components,
SearchBox's state has terms ( which has the search string )
When the user enters input SearchBox will update its state and possible to detect change event and fire ajax and get the response.
How SelectionBox can detect search that had happened, how it can get data.
This is why the state is moved to common ancestor FilterData.
[Ref: https://facebook.github.io/react/docs/lifting-state-up.html]
[Ref: https://facebook.github.io/react/docs/state-and-lifecycle.html#the-data-flows-down]
Code Sample -----------------------------------------------------
Selected values are not saved:
https://codepen.io/sudhnk/pen/NgWgqe?editors=1010
Selected values are saved:
https://codepen.io/sudhnk/pen/JJjyZw?editors=1010

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