Понимание реакции js-конструктора в дочернем компоненте - PullRequest
0 голосов
/ 30 октября 2019

Я хотел бы понять поведение конструктора реагирующих компонентов. Предположим, у меня есть три компонента - PageComponent, ListComponent, ItemComponent. Моя структура псевдокода имеет вид:

PageComponent (получать данные из редукса, извлекать данные)
ListComponent (получает данные как реквизиты, в цикле (map) отображает список ItemComponents)
ItemComponent (получает элементданные как реквизит, визуализация элемента, манипулирование данными)

Логика: - при изменении данных в ItemComponent изменения сохраняются в REDUX, и это изменение вызывает повторную визуализацию списка.

Вариант использования 1:
- PageComponent отображает ListComponent и ListComponent отображает список ItemComponets
- при изменении данных REDUX listItem, PageComponent обновляется, ListComponent обновляется и вызывается ItemComponent CONSTRUCTOR (его локальное состояние сбрасывается)

Use-case 2:
- PageComponent отображает только LIST (используя цикл отображения) ItemComponents.
- когда происходит изменение данных в REDUX listItem, PageComponent обновляется ItemComponent CONSTRUCTOR (компонент «только» обновляется) (и его локальное состояние)НЕ сбрасывается)

Почему в этих примерах другое поведение?

Исходный код: PageComponent:

import React from 'react'
...


class UsersPage extends React.Component {

    constructor(props) {
        super(props)
        props.actions.getUsers();
    }

    render() {

        const {users} = this.props


        return (
            <Main>
                {/* // NO ITEM CONSTRUCTOR IS CALLED
                    users.data.items.map((item, index) => {
                        return <ListItemComponent
                            data={item}
                            itemMethods={{
                                getItem: (data) => this.props.actions.getUser(data),
                                onEdit: (data) => this.props.actions.updateUser(data),
                                onDelete: (data) => this.props.actions.deleteUser(data),
                                validation: (data) => validateInput(this.props.strings, data)
                            }}
                            key={index}
                        />
                    })*/
                }
                { // ITEM CONSTRUCTOR IS CALLED
                <ListComponent
                    loading={users.isFetching}
                    data={users.data}
                    methods={{
                        getItem: (data) => this.props.actions.getUser(data),
                        onEdit: (data) => this.props.actions.updateUser(data),
                        onDelete: (data) => this.props.actions.deleteUser(data),
                        validation: (data) => validateInput(this.props.strings, data)
                    }}
                />}
            </Main>
        );
    }
}

UsersPage.propTypes = {
    users: PropTypes.object.isRequired,
    strings: PropTypes.object.isRequired,
}

function mapStateToProps(state) {
    return {
        users: state.users,
        strings: state.strings.data || {},
    };
}

function mapDispatchToProps(dispatch) {
    return {
        actions: bindActionCreators({
            getUsers,
            getUser,
            addUser,
            updateUser,
            deleteUser,
        }, dispatch)
    };
}

export default connect(
    mapStateToProps,
    mapDispatchToProps
)(withAlert(UsersPage));

ListComponent:

import React from 'react'
...

class ListComponent extends React.Component {

    getList() {
        return <div className="list-outer">
           <Row>
             {
              items.map((item, index) => {
                 return <ListItemComponent
                           data={item}
                           itemMethods={methods}
                           key={index}
                         />
              })
              }
           </Row>
        </div>
    }

    render() {
        const {loading} = this.props

        return (
            <div className="list-wrapper">
                {
                    loading ? <Spinner visible={true}/>
                        :
                        this.getList()
                }
            </div>
        )
    }
}


ListComponent.propTypes = {
    loading: PropTypes.bool.isRequired,
    data: PropTypes.object.isRequired,
    methods: PropTypes.object.isRequired,
}

export default ListComponent

ListItemComponent:

import React from 'react'
...

class ListItemComponent extends React.Component {

    constructor(props) {
        super(props)
        this.state = {
            editMode: false,
        }
    }

    toggleEditMode(){
        const editMode = this.state.editMode
        this.setState({editMode: !editMode})   
    }

    onEdit(id) {
        const itemMethods = this.props.itemMethods
        this.toggleEditMode()
        itemMethods.getItem({id: id})
    }

    onDelete(item) {
        //...
    }

    getFields(rowData, index) {
        return <div key={index}>
                {
                 rowData.map((itm, idx) => {
                   return <div key={idx}>{itm.label}: {itm.value}</div>
                 })
                }
            </div>
    }

    render() {
        const editMode = this.state.editMode
        const {data, itemMethods, strings} = this.props
        return (
            editMode ?
                <Form
                    id={data.id}
                    onSubmit={(data) => itemMethods.onEdit(data)}
                    validation={(data) => itemMethods.validation(data)}
                    onCloseForm={() => this.toggleEditMode()}
                />
                :
                <Col xs={12}>
                    <div>
                        <div
                            {this.getFields(data)}
                        </div>
                        <div className="controls">
                             <button
                                  className="btn btn-theme inverse danger"
                                  onClick={() => this.onDelete(data)}
                             >{strings.delete}</button>

                            <button
                                onClick={() => this.onEdit(data.id)}
                                className="btn btn-theme"               type="button"
                            >
                                {strings.edit}
                            </button>
                        </div>
                    </div>
                </Col>
        )
    }
}


ListItemComponent .propTypes = {
    strings: PropTypes.object.isRequired,
    data: PropTypes.object.isRequired,
    itemMethods: PropTypes.object.isRequired,
}


function mapStateToProps(state) {
    return {
        strings: state.strings.data || {}
    };
}


export default connect(
    mapStateToProps,
    null,
)(ListItemComponent )

Ответы [ 2 ]

0 голосов
/ 30 октября 2019

решено

Это было вызвано ListComponent и загрузочной опорой, которая была помещена как условие в функцию рендеринга. Когда элемент был отредактирован, для параметра prop loading было установлено значение true, счетчик стал видимым И это был единственный элемент в ListComponent, и поэтому элементы списка были размонтированы

0 голосов
/ 30 октября 2019

Убедитесь, что каждый ItemComponent имеет набор key проп. Когда React отображает ваш список элементов, он должен знать, как идентифицировать каждый элемент, и React оставляет это на ваше усмотрение. Если вы пропустите опцию key, React будет уничтожать и заново создавать ваш список при каждом повторном рендеринге, что означает вызов конструктора компонента.

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

Подробнее о списках и ключах можно прочитать здесь .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...