I'm working on a filter for a ListView, a way to be able to sort/order/etc the items. Basically I'm saving the parameters in state and they're updated via some toggles/select-fields on a <Modal>.
The modal has a cancel & apply button. If you select apply after changing filters, the ListView's contents would be updated. However if they were to select cancel after changing settings, they would be reverted to whatever it was before the filter modal was launched.
So I'm doing this:
// Update filterValues state
adjustFilterValue(filterSection, newValue) {
if ( this.state.hasAdjustedFilters === false ) {
const filterValues = this.state.filterValues;
this.setState({
hasAdjustedFilters: true
})
}
var newFilterValues = defaultFilterValues;
newFilterValues[filterSection] = newValue;
this.setState({
filterValues: newFilterValues
})
}
However whenever I adjust this.state.filterValues - newFilterValues get's updated too.
How can I save & isolate an object from state?
You can store your initial state by using lifecycle hooks:
componentDidMount() {
this._initialState = this.state;
}
Later on, in a case of revert, simply setState(this._initialState);
Related
I am trying to set an array to a state hook. Basically I want to keep a track of a per-row (of grid sort of) Edit Dialog Open State. Basically per row, I have a Edit button, launches a . As all seems rendered initially, I am trying to manage the show hide by keeping an array in the parent grid component. When user clicks on the Edit button, per row, I want to pass the rowData as props.data and want to provide the Edit functionality.
To keep the state of the editDialogs (show/hide), I am making a array of objects useState hook as follows:
const [editDialogsModalState, setEditDialogsModalState] = useState([{}]); // every edit dialog has it's own state
...
function initializeEditDialogsModalState(dataSet) {
let newState = [];
dataSet.map((item) => newState.push({ id: item.id, state: false }));
return setEditDialogsModalState(newState); // **PROBLEM->Not setting**
}
function addUDButtons(currentRowDataMovie) { // my edit/delete button UI code
const currRowDataId = currentRowDataMovie.id;
return (
<span>
<button
type="button"
className="btn btn-info"
onClick={() => setEditDialogsState(currRowDataId)}
>
Edit
</button>
{editDialogsModalState[currRowDataId].state && ( // **PROBLEM->null data even after set call**
<EditMovieComponent
open={editDialogsModalState[currRowDataId].state}
onToggle={toggleEditDialogsModalState(currentRowDataMovie)}
movie={currentRowDataMovie}
/>
)}
}
......
function buildGrid() {
{
if (!ready) {
// data is not there, why to build the grid
return;
}
initializeEditDialogsModalState(movies);
...........
}
However not able to get the editStates. A screen shot from debugger where I can see the movies (REST output), ready, but not the editDialogsModalState state array.
In general, is there a better ways of implementing such per-row basis functionality where on click of a button I want to open a React-bootstrap and pass the row-specific dataitem for doing operations ? (I am learning React, so may not not yet fully aware of all pointers).
Thanks,
Pradip
I have a dropdown where and i run a function onChange. After onChange i am filtering the current React state. This works, and i am able to filter on a unique value. However, i am not able to reset the state to the previous original state on change after switching to another dropdown select item.
handleStateOnChange = e => {
let selectedWerkgever = e.target.value;
const list = this.state.opleidingen.filter(({ opleiding_werkgever }) =>
selectedWerkgever.includes(opleiding_werkgever)
);
this.setState({
opleidingen: list,
isResetButtonActive: true,
});
console.log('changed');
};
I am filtering on everything inside the array that includes "opleiding_werkgever". But how can i first revert back on change, and re-filter again?
Switching to another dropdown on the same component or in the components sharing same state does not automatically reset the state. Your first call on handleStateChange filters the state and it will remain so until that component is unmounted. You could decide to retain the original opleidingen and then use it to reset the opleidingen when needed.
{
opleidingen: list,
isResetButtonActive: true,
originalOpleidingen : list
}
I would store selectedWerkgever in state and use that to filter your drop down elements. Keep the original list intact.
So simplify your handleStateOnChange:
handleStateOnChange = e => {
this.setState({
selectedWerkgever: e.target.value,
isResetButtonActive: true
});
};
And use this to filter your dropdown options:
<select>
{this.state.opleidingen.filter(({ opleiding_werkgever }) =>
this.state.selectedWerkgever.includes(opleiding_werkgever)).map(item=>
<Option value={item} />}>)
}
...
So the user makes changes to the state through an input element with this function:
handleCardChange = (event, style) => {
let updated = event.target.value;
this.setState(previousState => ({
styling: {
...previousState.styling,
styling: {
...previousState.styling.styling,
card: {
...previousState.styling.styling.card,
height: updated
}
}
}
}))
}
This updates the state fine, for example if the user puts "600px" in the field then the state will be 600px. Since the state has changed, I want to trigger a re-render of my child component since it uses its props.
I used a componentWillReceiveProps() for this like so:
componentWillReceiveProps(nextProps) {
console.log("componentWillRecieveProps: ", this.props.styling.styling.card.height);
this.state = {
styling: this.props.styling.styling
}
}
The problem I'm running into is that the child is behind the parent by one character at all times.
So if parents height is 600px, the childs value is 600p and I need to add another character in the input for the child to update.
Is there any way to fix this and make sure the child component will keep up to date with the parents current state?
I want to implement 'min-character-length' feature in react material-ui autocomplete component.
Below is the code .
constructor(props) {
super(props);
this.state = {
// based on this value, trying to maintain autocomplete's menu state open/close
shouldOpenList: false,
};
}
// Method in-built
onUpdateInput(searchText, dataSource, params) {
if( searchText && searchText.length >= 3) {
this.setState({
shouldOpenList: true
})
}
}
//component props
<AutoComplete
hintText={props.placeholder}
dataSource={ props.data }
dataSourceConfig={ {text: props.text, value: props.value} }
className="fabric-autocomplete form-control"
disableFocusRipple={false}
filter={filter}
onNewRequest={ this.onNewRequest.bind(this) }
onUpdateInput={ this.onUpdateInput.bind(this) }
open={this.state.shouldOpenList} // state's value used to show menu
/>
What I understand so far is function onUpdateInput() getting fired on typing each time and it is explicitly showing menu. Props 'open' is not able to deal with state 'shouldOpenList' value.
How do i achieve min-character-length feature for this component ?
thanks for help in advance.
Maybe you can try something like popoverProps={{style: {display: 'none'}}} and change that with state.
In the source of AutoComplete it keeps the bool open in it's state. Your open prop will only be set to the state on componentDidMount and in componentWillReceiveProps. In componentWillReceiveProps it checks for this.props.open !== nextProps.open.
So it checks for false !== false in this case, which does not trigger the setState. I dont really understand why they added this property since it seems a bit useless. Maybe only to open it on the initial render.
The internal handleChange of AutoComplete which calls onUpdateInput will set the components state to open every time a character is added. Completely ignoring your open property.
EDIT:
This solution works better
<AutoComplete
popoverProps={{
open: this.state.shouldOpenList
}}
hintText={props.placeholder}
dataSource={ props.data }
dataSourceConfig={ {text: props.text, value: props.value} }
className="fabric-autocomplete form-control"
disableFocusRipple={false}
filter={filter}
onNewRequest={ this.onNewRequest.bind(this) }
onUpdateInput={ this.onUpdateInput.bind(this) }
/>
But you will also need to set open to false if the length is less than 3.
I'm attempting to get the width of a ref DOM element and set state to then use within the Component render. The problem comes because this width changes on user input and when I try setState within componentDidUpdate it starts an infinite loop and my browsers bombs.
I created a fiddle here http://jsbin.com/dizomohaso/1/edit?js,output (open the console for some information)
My thinking was;
Component Mounts, setState: refs.element.clientWidth
User inputs data, triggers render
shouldComponentUpdate returns true only if new.state is not equal to old.state. My problem is, I'm not sure where makes sense to update this state?
Any help will be much appreciated, thanks for reading!
Brad.
var component = React.createClass({
componentDidMount: function() {
//Get initial width. Obviously, this will trigger a render,
//but nothing will change, look wise.
//But, if this is against personal taste then store this property
//in a different way
//But it'll complicate your determineWidth logic a bit.
this.setState({
elWidth: ReactDOM.findDOMNode(this.refs.the_input).getBoundingClientRect().width
})
},
determineWidth: function() {
var elWidth = ReactDOM.findDOMNode(this.refs.the_input).getBoundingClientRect().width
if (this.state.elWidth && this.state.elWidth !== elWidth) {
this.setState({
elWidth: elWidth
})
}
},
render: function() {
var styleProp = {}
if (this.state.elWidth) {
styleProp.style = { width: this.state.elWidth };
}
return (
<input ref="the_input" onChange={this.determineWidth} {...styleProp} />
)
}
})
I like to use .getBoundingClientRect().width because depending on your browser, the element might have a fractional width, and that width will return without any rounding.