The title could be clearer, but this is really the best I could come up with, sorry.
So, I am trying to create a filtered table component in React. However, I want the filter to be defined independently from the definition of the table itself. So, here is what I am doing.
I created a Filter component:
var Filter = React.createClass({
handleChange : function (value) {
this.props.updateTable(this.props.columnName,value);
},
render : function () {
//an input that will report its value to this.handleChange
}
});
Then, I create a Table component:
var Table = React.createClass({
filterChanged : function (column, value) {
//this will be wired as a updateTable prop for the Filter
},
render : function () {
//I am trying not to define a filter here,
//I am trying to use the previously-defined Filter component.
//I want the Table component to remain generic and re-usable,
//with optional filters.
var thisComponent = this;
//I can have as many filters as I want.
var filterToRender = React.Children.map(this.props.children, function (child) {
var filterUI;
if (child.type.displayName === 'Filter') {
filterUI = React.cloneElement(child, {updateTable : thisComponent.filterChanged});
}
return (<div>{filterUI}</div>);
});
//along with the rest of the table UI,
return (<div>
<table>bla bla</table>
{filterToRender}
</div>);
}
});
Then, in my main page, I render it like this:
ReactDOM.render( (<Table>
<Filter columnName='status'></Filter>
</Table>), document.getElementById('appHolder'));
It renders fine. The change functions also seem to be wired fine. However, I find that every time the filter value is changed, it triggers the Table's filterChanged method, increasing number of times. First change, it will trigger 2 times; second change, 6 times; 3rd change, 14 times.
Weird and uncanny. What am I doing wrong here?
The above procedure for doing things is correct as far as React is concerned. The bug I was getting was due to another framework (Materialize) that uses a jquery plugin to initialize some components. Since it mutates the DOM, I need to manually make sure the onChange events are properly attached to the right node.
Fwiw, I was attaching the onChange to this.handleChange in a drop-down list in the ComponentDidMount and ComponentDidUpdate of the Filter component. Removing initialization from ComponentDidUpdate, solved the problem. This, because multiple instances of handleChange were being bound to the onChange event each time the component updated.
Related
I am trying to implement a table in react where the user can edit individual rows by clicking the edit button on a row and then submit once he has made his change. I have say two components App.js and its child Table.js to implement this.
The way I thought of doing this initially was letting each of this component have their own state for rows and then the Table component reads from the props send to it by parent initially and only change the parent rows when users submits the change as oppose to onChange event. But I've read that reading props into state is an anti-pattern.
So decided to have everything in the parent by having two values for row (oldrows,newrows). And using them to maintain state instead, This is the design I came up with :
But what happens is whenever I click cancel the oldRows get bound to the newRows, here is a codePen example I put up:
https://codepen.io/snedden-gonsalves/pen/zYOVMWz
handleChangeRowInput = (event, keyValue) => {
let keyVals = [...this.state.newValuesArray];
keyVals[this.state.editIndex][keyValue] = event.currentTarget.value;
this.setState({
newValuesArray: keyVals
})
}
handleCancelRowInput = () => {
this.setState({
newValuesArray: [...this.state.oldValuesArray],
editIndex: -1
})
console.log('array', this.state.newValuesArray)
}
handleSubmitRowInput = () => {
this.setState({
oldValuesArray: [...this.state.newValuesArray],
editIndex: -1
})
}
In the codePen example if you enter a new value then cancel and then try adding a new value again the the old values and new values get bound.
I tried using lodash deepClone but it didn't work out, not sure why this is happening.
Also if you could comment on what is the best way to design this in react that would be awesome as I am very new to react and just trying to learn ..
I didn't find any issue after the cancel function. For me, the issue was coming up after I called the save function.
After clicking on the save button and then editing again, the old values and new values were get bound.
The handleSubmitRowInput function should create a new array for the oldValuesArray using the cloneDeep function
handleSubmitRowInput = () => {
this.setState({
oldValuesArray: _.cloneDeep(this.state.newValuesArray),
editIndex: -1
})
}
In short :-
I want to update child state first and than update parent state but react batches setSate and calls parent setState first and than childs. For more understanding read the explanation written below.
Basically i have a parent component which is having two child.
Sort component :- This component opens a dropdown for sort options selection. on click it should update the local state and than call the function passed as props from parent.
Product collection :- This component shows the products based on the sort selected.
I am passing a function (handleClick) from parent component to sort to get the value of selected sort into the parent and than passing it to product collection.
Since sort is a dropdown i want to close it first as soon as user selects a option and than i want to update the parent.
Right now i am using it in such a way, first updating local state and in callback calling the function passed from parent.
handleClick(param) {
this.setState({ selectedType: param.slug }, () =>
this.props.onHandleSort(param.slug)
)
}
But as written in React docs it batches the process and calls the parent setState first and than the child.
For example, that if both Parent and Child call setState
during a click event, Child isn’t re-rendered twice. Instead, React
“flushes” the state updates at the end of the browser event. This
results in significant performance improvements in larger apps.
I need it to happen in such way only because i need my dropdown to close first and than the parent should update.
I have tried the following this but nothing seems to work.
Made sort component stateless and dependent on the props from the parent but this will take time to close the dropdown.
Used callback of setState but since as written in docs it batches and calls parent setState first and than childs.
Judging from your codepen, you should lift the withRouter-wrapper up to the parent, let it figure out the selectedType and pass it to your sort component. In your onHandleSort, you can then set the new query.
class Parent extends Component {
// ...
handleClick (slug) => {
this.props.router.push({ query: { sorting: slug } })
}
// ...
render () {
const sorting = this.props.router && this.props.router.query
? this.props.router.query.sorting
: 'RELEVANCE';
return (
// ...
<Sort value={sorting} onHandleSort={this.handleClick} />
// ...
);
}
}
export default withRouter(Parent);
export default class Sort extends Component {
// ...
handleClick (param) => {
this.props.onHandleSort(param.slug)
}
// ...
render () {
const selectedType = this.props.sorting;
return (
// ...
);
}
}
I'm new to React. I'm not understanding a part in this code:
var HelloMessage = React.createClass({
render: function() {
return <h2>{this.props.message}</h2>;
}
});
var TextBox = React.createClass({
getInitialState: function() {
return { isEditing: false }
},
update: function() {
// Where is props.update defined? //
this.props.update(this.refs.messageTextBox.value);
this.setState(
{
isEditing: false
});
},
edit: function() {
this.setState({ isEditing: true});
},
In the code I can't find the props declaration for update. But looking through the code we should see "update" as a property of the TextBox component.
I'm not seeing an explicit declaration of this.props.update within any render method.
How/Where is props.update defined?
So inside the HelloReact component render method, a few TextBox components are returned like so:
...
<TextBox label='First Name' update={this.update.bind(null, 'firstName')}>
...
Now here what is happening is that HelloReact is passing a prop named update to this TextBox component. This means that inside the TextBox component I will be able to use this prop with this.props.update. Every prop that is passed down from the parent will populate this.props of the child. In this specific case we are passing down label and update.
Now inside the TextBox component we will be able to access these props intuitively with this.props.label and this.props.update. Inside this component it's define a private method that is called update, this is the code from the snippet you posted with better formatting:
...
update: function() {
this.props.update(this.refs.messageTextBox.value);
this.setState({ isEditing: false });
},
...
So here we are calling this.props.update which is the prop that was passed down from the parent in HelloReact. The reason we are wrapping this call in a private method is because on top of being able to call this.props.update() we also want to do something else, in this case we want to update the state of the TextBox component as well.
I hope this explanation was clear enough. I suggest reading about React from the official docs which are pretty amazing, or watch any of the many tutorials online. These are key concepts of React and you need to understand them properly in order to be able to develop in React.
For this case you might wanna read this, which comes from the offical docs and is about props.
I am using ReactJS, and I was wondering whether it is possible to highlight an element in the DOM when its value has been changed.
I have a list of elements whose values update periodically. While I have no problem animating in the DOM new items coming in to the list or items leaving the list using React's animation library interface, I am struggling to figure out how to detect the actual value change of the existing elements in the list.
Any ideas on how to do so? Thank you!
I had the same problem, then I decided to create a generic small module to handle it everywhere, that you can install from here
https://www.npmjs.com/package/react-change-highlight
using
yarn add react-change-highlight
what you can do to use it is to wrap you part that you want to highlight inside the component itself as follow
import ChangeHighlight from 'react-change-highlight';
export default () => {
const [count, setCount] = useState(0);
return (
<ChangeHighlight>
<div ref={React.createRef()}>{count}</div>
</ChangeHighlight>
);
}
and you will find the highlight on change as in the example below
I hope you find this useful
If your new values are coming down as new props, then you can use the componentWillReceiveProps lifecycle method on the component. It is called just before the component is updated.
Within that method, you can compare the new values (passed as the argument to the method) and the old values (available as this.props).
For instance:
componentWillReceiveProps: function(newProps) {
if(this.props.query != newProps.query) {
//handle change of query prop
//may include calls to this.setState()
}
}
Assuming that your elements are input elements, you need to use the onChange event listener. If you want to highlight that element, I would suggest having a CSS class that highlights the element and then conditionally applying it in render() based on state (or props if a child).
So for instance, you can have one component, add a handler function and the appropriate listener in the render():
handleChange: function(evt) {
//input was changed, update component state (or call parent method if child)
this.setState({changed: true});
},
render: function() {
return (<div>
<input type="text"
className={this.state.changed ? 'highlight' : ''}
onChange={this.handleChange}
/>
</div>);
}
That should set you in the right direction.
I created tabs in react and and now on click I have to change the class of the tabs the tabs classes may be as follows:
1:active
2:previousActive
3:alreadySelected
On click of a tab class become active and check whether it is selected before or not using alreadySelected class and active class from the last active tab is remove and if it is not alreadySelected then add alreadySelected.
Code of one tab in react:
var TabBody = React.createClass({
getInitialState: function() {
return {
class: 'tabBody tab activeTab'
}
},
render: function() {
a.tabBody = this;
return (React.createElement('div', {
className: this.state.class,
ref: 'body',
onClick: handleTabClick
},
React.createElement('span', {}, "Body"))
);
}
});
In order to change the class of the tabs I am doing in two ways and want to know which is effective. Code style one:
var bodyClass = (a.tabBody.state.class).split(' ');
var sleeveClass = (a.tabSleeve.state.class).split(' ');
var neckClass = (a.tabNeck.state.class).split(' ');
if (data === 'tabBody') {
bodyClass.push('activeTab');
var str1 = program.arrayToString(bodyClass);
Interfaces.tabBody.setState({
class: str1
});
}
Code Style 2
a.tabBody.refs.body.classList.remove('activeTab');
a.tabBody.refs.body.classList.add('tabPreviewComplete');
a.tabSleeve.refs.body.classList.add('activeTab');
Which style is good for doing this and why?
The point of react is that you do not need to/ should not update DOM directly. The idea behind react is that you render react components (virtual DOM), and that you let react figure out if and how to update DOM.
Changing classes using refs is a very risky strategy: Your component's state is then no longer in sync with actual DOM, which could bring you into debugging nightmares later on. So I would pose that Code Style 2 (even though it works) violates react principles.
One of the few exceptions for using refs, is to add a listener to a DOM component after it is mounted.
The react way is to put the classNames in state.
And do a setState() to update.
And let react do the DOM update,
which is very likely to be way faster, cleaner, and easier to maintain than getting refs, and changing classNames.
ref means you are using the actual DOM and setState means you are saying to react that please update the specific attribute of the component.
every thing is maintain by react.
On the other hand if you use refs it means you are doing every thing your own and react have no concern to your attributes and properties you are updating.