I have two stateless components and one stateful component. In my stateful component, I have a list of people and when I click one of them, it opens a modal. I basically store modal's state in stateful component and send the values as props to modal. So it changes states anytime it opens a model and re-renders everything.
How can I prevent my stateless classes to be re-rendered after I change state?
I have tried memo in my stateless components but no luck
Modal component
const modal = React.memo((props) => {
return (
<Modal show={props.show} onHide={props.handleClose} aria-labelledby="contained-modal-title-vcenter"
centered>
<Modal.Header closeButton>
<Modal.Title>Modal heading</Modal.Title>
</Modal.Header>
<Modal.Body>Woohoo, you're reading this text in a modal!</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={props.handleClose}>
Close
</Button>
</Modal.Footer>
</Modal>
)
});
export default modal
Person Component
const person = React.memo((props) => {
console.log("render");
return (
<article className="Person" onClick={props.clicked}>
<Container>
<Row>
<Col>
<p style={{marginBottom: 0 + 'px', marginTop: 1 + 'rem'}}><b>{props.name}</b></p>
<div className="Organization-wrapper">
<FontAwesomeIcon className="Organization-icon" icon={faBuilding}/><p>{props.company}</p>
</div>
</Col>
<Col>
<div>
{
props.image ?
<img src={props.image} height="50" width="50" className="Person-image"
alt="avatar"/>
: <svg height="50" width="50" className="Person-image">
<rect fill="cornflowerblue" x="0" y="0" height="50" width="50"/>
<text
fill="#ffffff"
fontSize="20"
textAnchor="middle"
x="25"
y="30">{props.first_char.toUpperCase()}</text>
</svg>
}
</div>
</Col>
</Row>
</Container>
</article>
);
});
Stateful Component
class PersonList extends Component {
state = {
persons: [],
show: false
};
componentDidMount() {
axios.get('')
.then(response => {
const result = response.data.data.slice(0, 5);
this.setState({persons: result});
});
}
personSelectedHandler = (id) => {
this.showModal();
};
showModal = () => {
this.setState({ show: true });
};
hideModal = () => {
this.setState({ show: false });
};
render() {
const persons = this.state.persons.map(person => {
let avatar;
if (person.picture_id != null) {
avatar = person.picture_id.pictures["128"];
}
return <ListGroup>
<ListGroup.Item>
<Person
key={person.id}
name={person.name}
company={person.org_id.name}
first_char={person.first_char}
image={avatar}
clicked={() => this.personSelectedHandler(person.id)}
/>
</ListGroup.Item>
</ListGroup>
});
return (
<div className="Wrapper">
<Modal show = {this.state.show} handleClose = {this.hideModal}/>
<Header/>
<div className="Breadcrumb">
<h4 className="Breadcrumb-text"><b>People's List</b></h4>
<hr/>
</div>
{persons}
</div>
);
}
}
export default PersonList;
<Person
...
clicked={() => this.personSelectedHandler(person.id)}
/>
This will define a new function reference for props.clicked
on each re-render of the parent function. This will cause the props for Person
to be changed on each render, causing Person
to also re-render.
Since you're not currently using id
in personSelectedHandler
, you could just omit the argument and keep the function reference constant:
clicked={this.personSelectedHandler}
If you end up needing the id
context, you might want to consider some use of the parents' state to achieve what you're after instead, if you really want to avoid re-rendering.
You could also convert Person
to a (pure) class component, and handle the callback from within that component.
Lastly, if push comes to shove and you absolutely must use this in-line arrow function, you could override React.memo
's comparison function, which is roughly analogous to defining componentShouldUpdate
for class components. From the docs:
function MyComponent(props) {
/* render using props */
}
function areEqual(prevProps, nextProps) {
/*
return true if passing nextProps to render would return
the same result as passing prevProps to render,
otherwise return false
*/
}
export default React.memo(MyComponent, areEqual);
There might be another way I'm not aware of to make this work, but hopefully this helps you understand the issue to explore alternate solutions. There's also some ideas and discussion here: Why shouldn't JSX props use arrow functions or bind?
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