Component class not rendering - reactjs

I'm having a weird problem with a self-made ReactJS component and I must be doing something fundamentally wrong.
I have a parent component, which periodically receives an array of objects by means of a REST service. The received elements are enumerated and put into a react-bootstrap ListGroup element:
<Row>
<Col sm={2}>
<ListGroup>
{this.state.maps.map((object, index) => {
return <ListGroupItem key={index}
onClick={() => this.setState({ currentMapSelection: object })}>
{object.map_name}
</ListGroupItem>
})}
</ListGroup>
</Col>
<Col sm={10}>
{this.renderMapForm()}
</Col>
</Row>
The display of the ListGroupItems is working fine.
Now I wanted to edit an List element by clicking on it. For code separation I wanted to put the entire edit logic into a separate component (MapLoadForm), which renders some Form elements and means to update and delete.
I put the rendering into a separate function, which conditionally renders the MapLoadForm:
renderMapForm() {
const mapForm = this.state.currentMapSelection;
if (mapForm != undefined) {
return <MapLoadForm map={mapForm}></MapLoadForm>
}
return <div></div>
}
The problem now is, that the MapLoadForm just renders correctly for the very first click. Subsequent clicks to other list items don't change it anymore. I don't see the call the the MapLoadForm constructor anymore.
If I replace the return <MapLoadForm map={mapForm}></MapLoadForm> by return <Button>{mapForm.map_name}</Button>, I see a new button rendered with every new click. The same happens, if I put the entire Form rendering logic into the return statement of the renderMapForm() function. Just the "outsourcing" into a new component doesn't work.
import React from 'react';
...
export class MapLoadForm extends React.Component {
constructor(props) {
super(props);
}
...
render() {
return (
<Form style={{ marginTop: "20px" }} horizontal>
...
</Form>
)
}
}
export default MapLoadForm;
Question now: Should that work or am I on the completely wrong track?
TIA

You are missing one part: Whenever props of any component changes, react re-render that component but re-rendering doesn't mean unmounting and mounting the component. It means same component will be rendered with new props, constructor will not get called again.
Put this console inside render method of MapLoadForm component, you will see new value each time component re-render:
console.log('props', this.props.map)
Solution:
What you are looking for is, componentWillReceiveProps lifecycle method, whenever component received new props, this lifecycle method will get called, do the calculation again with new props values, like this:
componentWillReceiveProps(nextProps){
console.log('new props', nextProps);
}
componentWillReceiveProps
componentWillReceiveProps() is invoked before a mounted component
receives new props. If you need to update the state in response to
prop changes (for example, to reset it), you may compare this.props
and nextProps and perform state transitions using this.setState() in
this method.

Related

How to get react grand-child node to render when child is out of my control

I have a react component which uses a 3rd party library Component as a Child node. The Grand children (or the children of the 3rd party libary) are under my control. When my component receives new props it re-renders, however the 3rd party component seems to stop my components grand-children from re-rendering also, even though the props my component received, are passed to the non-re-rendering components directly
If I remove the 3rd party component then my component re-renders as do the grand-children.
render() {
<div>
<ThirdPartyComponent props={blah}>
{this.props.products.map(prod => <MyGrandChildrenComponents product={prod} />
</ThirdPartyComponent>
</div>
}
A concrete example can be found on this code sandbox: codesandbox.io/s/silly-grass-7frlx
I'd expect my MyGrandChildrenComponents component to get updated when this.props.products changes... Any hints?
This can happen if ThirdPartyComponent is a stateful component and is not handling its prop updates correctly. One way to force a re-render is to add a key prop to your ThirdPartyComponent and update its value when a re-render is needed.
render() {
<div>
<ThirdPartyComponent key={something-that-changes-when-rerender-needed} props={blah}>
{this.props.products.map(prod => <MyGrandChildrenComponents product={prod} />
</ThirdPartyComponent>
</div>
}
If ThirdPartyComponent is a PureComponent it should re render (and so re render its children) when one of its props changes so you can try:
render() {
<div>
<ThirdPartyComponent props={blah} products={this.props.products}>
{this.props.products.map(prod => <MyGrandChildrenComponents product={prod} />
</ThirdPartyComponent>
</div>
}
To trigger re renders when this.props.products changes.
But any prop should do.
If ThirdPartyComponent has a custom implementation of shouldComponentUpdate, then you will have to find the specific prop which triggers update if it exists.

Pass data between siblings without Redux

Consider the following component tree
<Header />
<SearchBar />
<ProductList />
<Product />
In the SearchBar component I want to catch the value of an input and send that value to ProductList to dynamically render Product components based on the value received.
Is there a way to communicate between SearchBar and ProductList without the need of a component that wraps both of them or without Redux ?
According to React's page "React is all about one-way data flow down the component hierarchy" which means that data passing horizontally should be avoided.
In your case you could have a parent component that render <SearchBar/> and <ProductList/>based on state. For example, whenever the user enter with a value in <SearchBar> it changes the state on the parent component, and consequently <ProductList> will be rendered again.
As Osman said, you could pass the value of the SearchBar into a state and then from the state into the Product using a function.
handleChange(event) {
this.setState({
newProduct: event.currentTarget.value
)}
}
render() {
return(
<SearchBar onChange={this.handleChange} />
<Product content={this.state.newProduct} />
);
}
Make sure you don't forget to bind the function in the constructor.

Onclick an element that changes the state of another element

I'm trying to click on a sort icon that will trigger to change the order of a list.
To make it more simpler, let's say you have a button and another button and they are on separate divs from each other.
<div>
//Button 1
<button onclick={"some_click_handler"}>
</div>
<div>
//Button 2
<button>
{this.state.someToggle ? true : false}
</button>
</div>
Create a component which passes a callback to the button, this callback will update the state of the container which will in turn set the props of the list. This is very common in React and is the basis of how the compositional pattern works. If you need to share data between two components just put them in a container and lift the state to the parent component. These components are usually called containers and there is a bunch of documentation on it.
This is a good starting point: https://reactjs.org/docs/lifting-state-up.html
Something like this...
class Container extends React.Component {
constructor(props) {
super(props);
// Don't forget to bind the handler to the correct context
this.handleClick = this.handleClick.bind(this);
}
handleClick(sort) {
this.setState({sort: sort});
}
render() {
return (
<Button handleClick={this.handleClick} />
<List sort={this.state.sort} />
)
}
}

REACT Warning Unknown props parsing to child component [duplicate]

I've built my own custom react-bootstrap Popover component:
export default class MyPopover extends Component {
// ...
render() {
return (
<Popover {...this.props} >
// ....
</Popover>
);
}
}
The component is rendered like so:
// ... my different class ...
render() {
const popoverExample = (
<MyPopover id="my-first-popover" title="My Title">
my text
</MyPopover >
);
return (
<OverlayTrigger trigger="click" placement="top" overlay={popoverExample}>
<Button>Click Me</Button>
</OverlayTrigger>
);
}
Now, I want to add custom props to MyPopover component like that:
my text
And to use the new props to set some things in the popover
for example -
<Popover {...this.props} className={this.getClassName()}>
{this.showTheRightText(this.props)}
</Popover>
but then I get this warning in the browser:
Warning: Unknown props popoverType on tag. Remove these props from the element.
Now, I guess that I can just remove the {...this.props} part and insert all the original props one by one without the custom props, but In this way I lose the "fade" effect and also it's an ugly way to handle this problem. Is there an easier way to do it?
Updated answer (React v16 and older):
As of React v16, you can pass custom DOM attributes to a React Component. The problem/warning generated is no longer relevant. More info.
Original answer (React v15):
The easiest solution here is to simply remove the extra prop before sending it to the Popover component, and there's a convenient solution for doing that.
export default class MyPopover extends Component {
// ...
render() {
let newProps = Object.assign({}, this.props); //shallow copy the props
delete newProps.popoverType; //remove the "illegal" prop from our copy.
return (
<Popover {...newProps} >
// ....
</Popover>
);
}
}
Obviously you can (and probably should) create that variable outside your render() function as well.
Basically you can send any props you want to your own component, but you'd have to "clean" it before passing it through. All react-bootstrap components are cleansed from "illegal" props before being passed as attributes to the DOM, however it doesn't handle any custom props that you may have provided, hence why you have to do your own bit of housekeeping.
React started throwing this warning as of version 15.2.0. Here's what the documentation says about this:
The unknown-prop warning will fire if you attempt to render a DOM element with a prop that is not recognized by React as a legal DOM attribute/property. You should ensure that your DOM elements do not have spurious props floating around.
[...]
To fix this, composite components should "consume" any prop that is intended for the composite component and not intended for the child component.
For further reading, check this page from the official react site.

Add custom props to a custom component

I've built my own custom react-bootstrap Popover component:
export default class MyPopover extends Component {
// ...
render() {
return (
<Popover {...this.props} >
// ....
</Popover>
);
}
}
The component is rendered like so:
// ... my different class ...
render() {
const popoverExample = (
<MyPopover id="my-first-popover" title="My Title">
my text
</MyPopover >
);
return (
<OverlayTrigger trigger="click" placement="top" overlay={popoverExample}>
<Button>Click Me</Button>
</OverlayTrigger>
);
}
Now, I want to add custom props to MyPopover component like that:
my text
And to use the new props to set some things in the popover
for example -
<Popover {...this.props} className={this.getClassName()}>
{this.showTheRightText(this.props)}
</Popover>
but then I get this warning in the browser:
Warning: Unknown props popoverType on tag. Remove these props from the element.
Now, I guess that I can just remove the {...this.props} part and insert all the original props one by one without the custom props, but In this way I lose the "fade" effect and also it's an ugly way to handle this problem. Is there an easier way to do it?
Updated answer (React v16 and older):
As of React v16, you can pass custom DOM attributes to a React Component. The problem/warning generated is no longer relevant. More info.
Original answer (React v15):
The easiest solution here is to simply remove the extra prop before sending it to the Popover component, and there's a convenient solution for doing that.
export default class MyPopover extends Component {
// ...
render() {
let newProps = Object.assign({}, this.props); //shallow copy the props
delete newProps.popoverType; //remove the "illegal" prop from our copy.
return (
<Popover {...newProps} >
// ....
</Popover>
);
}
}
Obviously you can (and probably should) create that variable outside your render() function as well.
Basically you can send any props you want to your own component, but you'd have to "clean" it before passing it through. All react-bootstrap components are cleansed from "illegal" props before being passed as attributes to the DOM, however it doesn't handle any custom props that you may have provided, hence why you have to do your own bit of housekeeping.
React started throwing this warning as of version 15.2.0. Here's what the documentation says about this:
The unknown-prop warning will fire if you attempt to render a DOM element with a prop that is not recognized by React as a legal DOM attribute/property. You should ensure that your DOM elements do not have spurious props floating around.
[...]
To fix this, composite components should "consume" any prop that is intended for the composite component and not intended for the child component.
For further reading, check this page from the official react site.

Resources