我正在我的应用程序中实现日记功能,并且我有一个主JournalComponent
,其中有一个SectionList
,可以渲染许多JournalEntriesComponent
(又称一天)。
当您按JournalEntriesComponent
时,将展开所述条目的文本以全部阅读。这由this.state.isTextExpanded
的本地状态管理。
我的问题是,当我在PARENT组件中执行this.setState({})
时,我的孩子的JournalEntriesComponent
状态将重置,即this.state.isTextExpanded
键变为false
(又名默认值)。
在我的父组件中,我有一个onHandleScroll
方法来隐藏某些东西,这导致了我的问题,但是我也注意到与其他事物相同的行为。
我的问题是:当我的父级组件调用this.setState({})
时,如何避免重置孩子的状态?
我的代码:
儿童组件:
class JournalEntryComponent extends PureComponent {
constructor(props) {
super(props)
if (Platform.OS === 'android') {
UIManager.setLayoutAnimationEnabledExperimental &&
UIManager.setLayoutAnimationEnabledExperimental(true)
}
this.state = { isTextExpanded: false }
this.onExpand = this.onExpand.bind(this)
this.onRegenerateData = this.onRegenerateData.bind(this)
this.deriveColorGradientFromRating = this.deriveColorGradientFromRating.bind(
this
)
}
onRegenerateData() {
return this.props.generateSectionListData()
}
onExpand() {
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut)
return this.setState({ isTextExpanded: !this.state.isTextExpanded })
}
deriveColorGradientFromRating(rating) {
// decide wether to show custom moood or a default mood by looking into settings in props
const {
colorPalette,
customMoodSelected,
shouldUseDefaultColorPalettes,
} = this.props.settings.moodColorCategory
const mapRatingToArrayIndex = { [5]: 0, [4]: 1, [3]: 2, [2]: 3, [1]: 4 }
if (!shouldUseDefaultColorPalettes) {
// find the correct custom color palette and return array with colors
let relevantArrayOfGradients = this.props.settings.moodColorCategory[
customMoodSelected
]
let darkColor =
relevantArrayOfGradients[mapRatingToArrayIndex[rating]].dark
let lightColor =
relevantArrayOfGradients[mapRatingToArrayIndex[rating]].light
return [lightColor, darkColor]
}
// else return color gradients from default color palettes
return COLOR_MAP[colorPalette].colorMap[rating].colors
}
render() {
const { isTextExpanded } = this.state
const { element, isExpandedFromParent, onDisplayDayMoodPress } = this.props
console.log('isTextExpanded', isTextExpanded)
return (
<View style={styles.sectionEntryContainer}>
<View style={styles.sectionDayContainer}>
<LinearGradient
style={styles.gradientDayStyle}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 0 }}
colors={this.deriveColorGradientFromRating(
element.item.data.rating
)}
>
{Platform.OS === 'ios' ? (
<View style={styles.blackLayer}>
<Text style={styles.sectionDayText} numberOfLines={1}>
{element.item.day}
</Text>
</View>
) : (
<View style={styles.blackLayerAndroid}>
<Text style={styles.sectionDayTextAndroid} numberOfLines={1}>
{element.item.day}
</Text>
</View>
)}
</LinearGradient>
</View>
<View style={styles.sectionDayDescriptionContainer}>
<TouchableWithoutFeedback onPress={this.onExpand}>
<LinearGradient
style={styles.gradientDescriptionStyle}
start={{ x: 0, y: 1 }}
end={{ x: 0, y: 0 }}
colors={['#d4d5d6', '#eef2f3']}
>
<Text
style={styles.sectionDescriptionText}
numberOfLines={
isTextExpanded || isExpandedFromParent ? null : 2
}
>
{element.item.data.optionalDescription}
</Text>
</LinearGradient>
</TouchableWithoutFeedback>
{isTextExpanded ? (
<TouchableOpacity
style={styles.gradientButtonContainer}
onPress={() =>
onDisplayDayMoodPress(element.item.day, element.item.month)
}
>
<LinearGradient
style={styles.gradientButtonStyle}
start={{ x: 0, y: 1 }}
end={{ x: 0, y: 0 }}
colors={['#d4d5d6', '#eef2f3']}
>
<Icon name={'ios-arrow-forward'} style={styles.arrowStyle} />
</LinearGradient>
</TouchableOpacity>
) : null}
</View>
</View>
)
}
}
const mapStateToProps = state => {
return {
settings: state.settings,
}
}
export default JournalEntryComponent
父项:
class JournalComponent extends PureComponent {
constructor(props) {
super(props)
if (Platform.OS === 'android') {
UIManager.setLayoutAnimationEnabledExperimental &&
UIManager.setLayoutAnimationEnabledExperimental(true)
}
this.state = {
sectionListData: [],
expandAllEntries: false,
emotionSurveyDateToDisplay: null,
isShowEmotionSurveyVisible: false,
isCloseJournalModeButtonVisible: true,
}
this.onHandleScroll = this.onHandleScroll.bind(this)
this.renderMonthHeader = this.renderMonthHeader.bind(this)
this.renderMonthEntries = this.renderMonthEntries.bind(this)
this.onExpandAllEntries = this.onExpandAllEntries.bind(this)
this.onCloseJournalMode = this.onCloseJournalMode.bind(this)
this.onDisplayDayMoodPress = this.onDisplayDayMoodPress.bind(this)
this.generateSectionListData = this.generateSectionListData.bind(this)
}
componentWillMount() {
return this.generateSectionListData()
}
componentWillReceiveProps(nextProps) {
if (
this.props.moods[moment().format('YYYY')] !==
nextProps[moment().format('YYYY')]
) {
return this.generateSectionListData(
nextProps.moods[moment().format('YYYY')]
)
}
}
onHandleScroll(scrollEvent) {
console.log(scrollEvent.nativeEvent.contentOffset.y)
if (scrollEvent.nativeEvent.contentOffset.y > height * 2) {
if (this.state.isCloseJournalModeButtonVisible) {
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut)
return this.setState({ isCloseJournalModeButtonVisible: false })
}
} else if (scrollEvent.nativeEvent.contentOffset.y < height * 2) {
if (!this.state.isCloseJournalModeButtonVisible) {
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut)
return this.setState({ isCloseJournalModeButtonVisible: true })
}
}
}
onExpandAllEntries() {
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut)
return this.setState({ expandAllEntries: !this.state.expandAllEntries })
}
onCloseJournalMode() {
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut)
return this.props.onCloseJournalMode()
}
renderMonthHeader(el) {
return (
<View style={styles.sectionListHeaderContainer}>
<Text style={styles.sectionListHeaderText}>{el.section.title}</Text>
</View>
)
}
renderMonthEntries(el) {
return (
<JournalEntryComponent
element={el}
onDisplayDayMoodPress={this.onDisplayDayMoodPress}
isExpandedFromParent={this.state.expandAllEntries}
generateSectionListData={this.generateSectionListData}
/>
)
}
render() {
const hasNotch = DeviceInfo.hasNotch()
return (
<View style={styles.contentContainer}>
<View style={styles.contentContainer}>
<View style={styles.mainHeaderContainer}>
<View style={styles.sideHeaderButtonContainer}>
{hasNotch ? (
<Text style={styles.mainSideBarTitleText}>
{moment().format('Do / M')}
</Text>
) : null}
</View>
<View style={styles.mainHeaderTitleContainer}>
{hasNotch ? null : (
<Text style={styles.mainHeaderTitleText}>
{strings.JournalComponent.journalTitle.title}
</Text>
)}
</View>
<TouchableOpacity
style={styles.sideHeaderButtonContainer}
onPress={this.onExpandAllEntries}
>
<Icon
name={
this.state.expandAllEntries ? 'ios-contract' : 'ios-expand'
}
style={styles.expandIcon}
/>
</TouchableOpacity>
</View>
<View style={styles.contentContainer}>
{this.state.sectionListData.length === 0 ? (
<View style={styles.noDataContainer}>
<Text style={styles.noEntriesText}>
{strings.JournalComponent.noEntries.message1}
</Text>
<Text style={styles.noEntriesText}>
{strings.JournalComponent.noEntries.message2}
</Text>
</View>
) : (
<SectionList
onMomentumScrollBegin={e =>
console.log('onMomentumScrollBegin', e)
}
onScroll={this.onHandleScroll}
stickySectionHeadersEnabled={false}
renderItem={this.renderMonthEntries}
renderSectionHeader={this.renderMonthHeader}
sections={this.state.sectionListData}
keyExtractor={(item, index) =>
`index-${index}-${Math.random() / 100}`
}
/>
)}
</View>
</View>
{this.state.isCloseJournalModeButtonVisible ? (
<TouchableOpacity
style={styles.closeModalContainer}
onPress={this.onCloseJournalMode}
>
<View style={styles.closeModalIconBorder}>
<Icon name="ios-close" style={styles.closeModalIcon} />
</View>
</TouchableOpacity>
) : null}
</View>
)
}
}
export default JournalComponent
答案 0 :(得分:2)
您的keyExtractor
函数导致了此问题。您使用math.random
会在每个渲染中生成一个新密钥。
每个项目都应具有唯一且稳定的密钥,否则React会卸载并重新渲染它,从而导致您重置状态。
使用keyExtractor
根据条目标识提供稳定的值。
答案 1 :(得分:0)
您需要将JournalEntryComponent创建为干燥(无状态)组件,然后它才能工作。对于要从此JournalEntryComponent触发的任何方法,应从父级调用方法。