Я пишу редактируемый пользователем компонент, который поддерживает пользовательские изменения в своем состоянии. Я хочу иметь возможность использовать компонент двумя способами: 1: Жестко запрограммирован автором по одному, или 2: , сгенерированный из массива из состояния родительского компонента. У меня проблемы с синхронизацией состояний во втором сценарии. Я хочу, чтобы компонент был съемным, поэтому у него есть кнопка «удалить меня», которая должна связываться с состоянием родителя с помощью функции обратного вызова prop.
Сценарий:
Позвольте сказать У меня есть родительский компонент, который имеет массив в своем состоянии. Из этого массива дочерние компоненты отображаются с помощью оператора .map
:
// in ParentComponent.js:
state = {
markers: [
{coords: Array(2), popupContent: "Popup 1"},
{coords: Array(2), popupContent: "Popup 2"},
{coords: Array(2), popupContent: "Popup 3"},
... etc ...
]
}
// In the return:
this.state.markers.map( (marker, index) => (
<Marker key={index}>
<Popup sourceKey={index}
setContentCallback={this.saveContentToState}
removalCallback={this.removalCallback} >
{marker.popupContent}
</Popup>
</Marker>
))
Компонентом, о котором идет речь, является granchild, <Popup/>
. Из Popup. js пользователь может вносить изменения в содержимое компонента, и эти изменения сохраняются в состоянии Popup:
// In Popup.js
state = {
content: this.props.children,
inputValue: this.props.children
}
onEditHandler = {
this.setState({inputValue: e.target.value})
}
saveEdits = () => {
if (this.props.saveContentCallback){
this.props.saveContentCallback(this.state.inputValue, this.props.sourceKey)
}
this.setState({
content: this.state.inputValue,
})
}
removeSource = () => {
if(this.props.removalCallback){
this.props.removalCallback(this.props.sourceKey)
} else {
// internal leaflet function to remove a popup from a map
this.thePopup.leafletElement._source.remove()
}
}
// Within the return function:
return (
<>
<ContentEditableDiv onChange={this.onEditHandler}>
{ this.state.content }
</ContentEditableDiv>
<div onClick={this.saveEdits}>Save</div>
<div onClick={this.removeSource}>Remove me</div>
</>
)
Вы можете увидеть, как компонент будет поддерживать изменения в своем собственном состоянии. из функции saveEdits
в любом из сценариев ios, упомянутых выше. Но для того, чтобы сообщать об изменениях с состоянием родителя, он использует реквизиты removalCallback
и saveContentCallback
. Итак, вернемся к ParentComponent.js
,
removalCallback = index => {
mapRef.current.leafletElement.closePopup()
this.setState(prevState => {
prevState.markers.splice(index, 1)
return {
markers: prevState.markers
}
})
}
saveContentToState = (content, index) => {
this.setState( prevState => {
const newMarkers = prevState.markers
newMarkers[index].popupContent = content
return {
...this.state.newMarkers
}
})
}
Ожидаемое поведение
Когда во всплывающем окне нажата кнопка «Удалить», я ожидаю, что будет вызван обратный вызов. Когда вызывается обратный вызов, он должен удалить это всплывающее окно из «маркеров» массива состояний ParentComponent, а ParentComponent должен выполнить повторную визуализацию только с оставшимися маркерами с их popupContent
. Так, например, если я начну с этого массива:
state = {
markers: [
{coords: Array(2), popupContent: "Popup 1"},
{coords: Array(2), popupContent: "Popup 2"},
{coords: Array(2), popupContent: "Popup 3"},
]
}
, а затем нажмите кнопку remove me
во всплывающем окне 2, я должен получить этот массив:
state = {
markers: [
{coords: Array(2), popupContent: "Popup 1"},
{coords: Array(2), popupContent: "Popup 3"},
]
}
С два маркера с всплывающими окнами с надписью «Всплывающее окно 1» и «Всплывающее окно 3».
Фактическое поведение - ошибка
Итак, я действительно получаю массив ожидаемых в состоянии ParentComponent, так как я просто написал выше. Тем не менее, внутренние состояния всплывающих окон не сотрудничают. Когда я нажимаю кнопку remove me
в всплывающем окне № 2, я получаю 2 всплывающих окна, но их содержимое - «Всплывающее окно 1» и «Всплывающее окно 2». Когда я смотрю на внутреннее состояние каждого <Popup />
компонента, content
для каждого - это «Popup 1» и «Popup 2» соответственно. Как будто при удалении i
th всплывающего окна его внутреннее состояние каким-то образом передается в i+1
th всплывающего окна, которое передается через все всплывающие окна в массиве.
Это проект листовки с реакцией, но я чувствую, что это вопрос управления состоянием реагирования. Откройте визуализацию кодов и окно, и вы увидите 5 всплывающих окон. Когда вы нажмете «удалить меня» в любом всплывающем окне (кроме последнего), вы увидите сдвиг всех номеров всплывающего окна. На вкладке компонентов реагирующих инструментов вы увидите, что массив состояний <Map />
(т.е. <ParentComponent>
) обновляется правильно. Но, глядя на каждое внутреннее состояние <EditablePopup />
, они не соответствуют состоянию родительского (<Map
>) компонента. Я знаю, что что-то вроде state = { content: this.props.something }
может вызвать проблемы, но я не уверен, что это виновник в этом сценарии.
Что здесь не так? Разве все эти <Marker/>
и <Popup/>
компоненты не должны перегенерироваться каждый раз при срабатывании removalCallback
или saveContentCallback
, так как это обновляет состояние родителя и должно вызывать рендеринг родителя? Я попытался добавить this.forceUpdate
в эти обратные вызовы в родительском компоненте, но это ничего не сделало. Извините за длинный вопрос, спасибо за чтение .