In a react.js application, I’m wondering what the best practices are to give each component an ID that can be used to update only that component’s information as needed.
For example, if we have a component that displays sales information and we create and display 20 of them because we have 20 products, then at an interval we retrieve JSON from our server to query for any products that have updated, then wish to only update those that changed, how could we accomplish this.
In the past, we would use PHP to create widgets and give them id attributes based on an ID (like “sales” + productid). Then, when the JSON returns from our server with productIDs that changed, we could target only the widgets that are needed to be updated. We would know their id attribute and could update them with something like:
this.element.find(“#” + “sales” + productid).html(“the changed html here”)
(yes, it was cumbersome)
In React, how can one still have this type of control to update only components that are based on some meaningful ID returned from a server or database? Is there a way to give an ID to a component and use/target it later?
Below is a simple example that simulates fetching a list of products every second and will render the list. The product ID is used as a key in the rendered list and will allow React to efficiently re-render the list on every update.
class ProductsInfoContainer extends React.Component {
constructor(props) {
super(props);
this.state = { products: [] };
this.fetchProducts = this.fetchProducts.bind(this);
}
componentDidMount() {
this.refreshTimer = setInterval(this.fetchProducts, 1000);
}
componentWillUnmount() {
clearInterval(this.refreshTimer);
}
fetchProducts() {
//simulate API fetch from server - we'll just create a list with random prices
const products = [
{id: "123-456", name: "Widget", price: this.getPrice()},
{id: "234-567", name: "Widget Spinner", price: this.getPrice()},
{id: "345-678", name: "Fidget", price: this.getPrice()},
{id: "456-789", name: "Fidget Spinner", price: this.getPrice()},
];
this.setState({ products });
}
getPrice() {
return (Math.random() * 100).toFixed(2);
}
render() {
return <ProductsInfo products={this.state.products} />;
}
}
const ProductsInfo = ({ products }) => (
<table>
<thead>
<tr>
<th>Id</th>
<th>Name</th>
<th>Price</th>
</tr>
</thead>
<tbody>
{products.map(product =>
<tr key={product.id}>
<td>{product.id}</td>
<td>{product.name}</td>
<td>{product.price}</td>
</tr>
)}
</tbody>
</table>
);
ReactDOM.render(<ProductsInfoContainer />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.6.1/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.6.1/react-dom.min.js"></script>
<div id="root"></div>
When your products
data updates come from the server, the state change in the container will cause React to re-render your ProductsInfo
component and will work out what has changed and update accordingly. Providing a key
for each product makes that process a lot more efficient.
Clearly this is a trivial example, but it gives you a good idea. You are declaratively defining how the UI should look given your input data and let React worry about how to efficiently translate that into something on screen. You can get a ref to a DOM node in React and update it directly, but that completely goes against the grain of what React is about and should be used sparingly IMO.
If you need to do something more complex with how you render each product, you could define another component and render that from your map function e.g.
{products.map(product => <Product key={product.id} info={product}/>)}
If you were talking about thousands (or more) of products, then perhaps you would take a different approach, or at the very least consider paging the data or the like.
Please feel free to ask any questions if anything is not clear.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With