父状态更改时如何防止子组件重新渲染

问题描述 投票:1回答:1

我有两个无状态组件和一个有状态组件。在我的有状态组件中,我有一个人员列表,当我点击其中一个时,它会打开一个模态。我基本上将模态的状态存储在有状态组件中,并将值作为道具发送到模态。因此,它会在打开模型并重新渲染所有内容时随时更改状态。

在更改状态后,如何防止无状态类重新呈现?

我在无状态组件中尝试过备忘录,但没有运气

模态组件

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

人员组件

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>
    );
});

有状态组件

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;
reactjs frontend
1个回答
1
投票
<Person
    ...
    clicked={() => this.personSelectedHandler(person.id)}
/>

这将在每次重新渲染父函数时为props.clicked定义一个新的函数引用。这将导致Person的道具在每次渲染时被更改,导致Person也重新渲染。

既然你目前没有在id中使用personSelectedHandler,你可以省略参数并保持函数引用不变:

clicked={this.personSelectedHandler}

如果你最终需要id上下文,你可能想要考虑使用父母的状态来实现你所追求的目标,如果你真的想避免重新渲染。

您还可以将Person转换为(纯)类组件,并处理该组件内的回调。

最后,如果push推进并且你绝对必须使用这个内联箭头函数,你可以覆盖React.memo的比较函数,这大致类似于为类组件定义componentShouldUpdateFrom 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);

可能还有另一种方法我不知道如何使这项工作,但希望这有助于您了解探索替代解决方案的问题。这里也有一些想法和讨论:Why shouldn't JSX props use arrow functions or bind?

© www.soinside.com 2019 - 2024. All rights reserved.