У меня есть два компонента без сохранения состояния и один компонент с сохранением состояния. В моем компоненте с состоянием у меня есть список людей, и когда я щелкаю по одному из них, он открывает модальное окно. Я в основном сохраняю модальное состояние в компоненте с сохранением состояния и отправляю значения как реквизиты модальному. Таким образом, он меняет состояния каждый раз, когда открывает модель и перерисовывает все.

Как я могу предотвратить повторную визуализацию моих классов без состояния после изменения состояния?

Я пробовал записку в моих компонентах без сохранения состояния, но не повезло

Модальный компонент

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;
3
wolf 19 Апр 2019 в 03:31

2 ответа

Лучший ответ
<Person
    ...
    clicked={() => this.personSelectedHandler(person.id)}
/>

Это определит новую ссылку на функцию для props.clicked при каждом повторном рендеринге родительской функции. Это приведет к тому, что реквизиты для Person будут меняться при каждом рендеринге, в результате чего Person также будет повторным рендерингом.

Поскольку в настоящее время вы не используете id в personSelectedHandler, вы можете просто опустить аргумент и сохранить константу ссылки на функцию:

clicked={this.personSelectedHandler}

Если вам в конечном итоге понадобится контекст id, вы можете рассмотреть возможность использования состояния родителей для достижения желаемого, если вы действительно хотите избежать повторного рендеринга ,

Вы также можете преобразовать Person в (чистый) компонент класса и обработать обратный вызов из этого компонента.

И наконец, если push приходит к толчку, и вы обязательно должны использовать эту встроенную функцию стрелки, вы можете переопределить функцию сравнения React.memo, которая примерно аналогична определению componentShouldUpdate для компонентов класса. Из документов:

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

Возможно, есть и другой способ заставить эту работу работать, но, надеюсь, это поможет вам понять проблему и найти альтернативные решения. Здесь также есть некоторые идеи и обсуждения: Почему JSX не поддерживает использовать функции стрелок или связывать?

1
Dylan Walker 19 Апр 2019 в 15:58

Если вы используете ловушку, вы можете использовать метод useCallback() вместе с Recat.memo. в вашем компоненте:

const callBack = useCallback(() => this.personSelectedHandler(person.id),[person.id]);

useCallback((person.id) => personSelectedHandler(person.id),[person.id]); всегда возвращает один и тот же экземпляр функции, если person.id одинаков. затем:

<Person
    ...
    clicked={callBack}
/>
1
sably 19 Дек 2019 в 16:59