Reaction-native: отправить полное состояние из одного компонента в другой?
У меня есть Reminder component
состоящий из формы, где я храню текст и дату по нажатию кнопки, используя AsyncStorage
,
Теперь я хочу отобразить эти сохраненные данные в Agenda Component
,
я использую Agenda component
от react-native-calendars
библиотека реагировать на родные календари
это мое reminder component
class Reminder extends Component {
constructor(props) {
super(props);
this.state = {
input: '',
chosenDate: new Date(),
};
this.setDate = this.setDate.bind(this);
this.handleChangeInput = this.handleChangeInput.bind(this);
this.saveData = this.saveData.bind(this);
}
setDate(newDate) {
this.setState({
chosenDate: newDate
});
}
handleChangeInput = (text) => {
this.setState({input:text});
}
//save the input
saveData() {
AsyncStorage.setItem("key", JSON.stringify(this.state));
}
render() {
return (
<View>
<Form style={styles.formContainer}>
<View style={styles.formView}>
< TextInput
placeholder = "Set your reminder"
onChangeText={this.handleChangeInput}
value={this.state.input}
/>
<DatePicker
defaultDate={new Date()}
minimumDate={new Date(2018, 1, 1)}
maximumDate={new Date(2019, 12, 31)}
locale={"en"}
timeZoneOffsetInMinutes={undefined}
modalTransparent={false}
animationType={"fade"}
androidMode={"default"}
placeHolderText="Select date"
textStyle={{ color: "green" }}
placeHolderTextStyle={{ color: "#d3d3d3" }}
onDateChange={this.setDate}
/>
<Text style={styles.datePicker}>
{this.state.chosenDate.toString().substring(0,10)}
</Text>
</View>
<View style={styles.footer}>
<Button block success style={styles.saveBtn}
onPress={ () =>
{
this.saveData()
console.log('save data',this.state);
}
}
>
<Icon type='MaterialIcons' name='done' />
</Button>
</View>
</Form>
</View>
);
}
}
export default Reminder;
и это экран Reminder screen
import React, { Component } from 'react';
import { View, StatusBar } from 'react-native';
import PropTypes from 'prop-types';
import Reminder from '../components/Reminder';
const ReminderScreen = ({navigation}) => (
<View >
<Reminder navigation={navigation} >
<StatusBar backgroundColor = "#28F1A6" />
</Reminder >
</View>
);
Reminder.propTypes = {
navigation: PropTypes.object.isRequired
}
export default ReminderScreen;
и это компонент, который я хочу отобразить эти данные Agenda Component
class WeeklyAgenda extends Component {
constructor(props) {
super(props);
this.state = {
items: {},
selectedDate: ''
};
}
render() {
return (
<View style={{height:600}}>
<Agenda
items={this.state.items}
loadItemsForMonth={this.loadItems.bind(this)}
selected={this.props.day}
renderItem={this.renderItem.bind(this)}
renderEmptyData={this.renderEmptyDate.bind(this)}
rowHasChanged={this.rowHasChanged.bind(this)}
onRefresh = {() => { this.setState({refeshing : true})}}
refreshing = {this.state.refreshing}
refreshControl = {null}
pastScrollRange={1}
futureScrollRange = {3}
theme = {
{
agendaTodayColor: '#28F1A6',
agendaKnobColor: '#28F1A6',
dotColor: '#28F1A6',
selectedDayBackgroundColor: '#28F1A6',
todayTextColor: '#28F1A6',
}
}
/>
<View >
<Fab
active={!this.state.active}
direction="up"
style={{ backgroundColor: '#28F1A6'}}
position = 'bottomRight'
onPress={() => this.props.navigation.navigate('Reminder')}>
<Icon type='MaterialCommunityIcons' name="reminder" />
</Fab>
</View>
</View>
);
}
//On application loads, this will get the already saved data and set the state true when it's true.
componentDidMount() {
AsyncStorage.getItem("key").then((newItems) => {
this.setState(JSON.parse(newItems));
});
}
loadItems = (day) => {
console.log('day',day);
console.log('items', this.state.items);
const {selectedDate} = this.state;
setTimeout(() => {
console.log('selected date', selectedDate);
this.setState({selectedDate: day});
console.log('selected date later', day);
const newItems = {};
Object.keys(this.state.items).forEach(key => {newItems[key] = this.state.items[key];});
console.log('new items later', newItems);
this.setState({
items: newItems
});
console.log('new items later', this.state.newItems);
console.log('items later', this.state.items);
this.state.items;
},1000);
};
renderItem(item) {
return (
<View style={[styles.item, {height: item.height}]}>
<TouchableOpacity onPress={() => {this.props.navigation.navigate('Reminder')}}>
<Text>{item.name}</Text>
</TouchableOpacity>
</View>
);
}
renderEmptyDate() {
return (
<View style={styles.emptyDate}>
<TouchableOpacity onPress={() => {this.props.navigation.navigate('Reminder')}}>
<Text style={styles.emptyTextColor}> No Event or Reminder on this date </Text>
</TouchableOpacity>
</View>
);
}
rowHasChanged(r1, r2) {
return r1.name !== r2.name;
}
timeToString(time) {
const date = new Date(time);
return date.toISOString().split('T')[0];
}
}
export default WeeklyAgenda;
и это его экран Agenda Screen
import React, { Component } from 'react';
import { View, Text, StatusBar } from 'react-native';
import PropTypes from 'prop-types';
import WeeklyAgenda from '../components/Agenda';
class AgendaScreen extends Component {
state = { }
render() {
const {navigation} = this.props;
const { params } = this.props.navigation.state;
return (
<View style={{height: 100}}>
<WeeklyAgenda day={params["day"]} navigation={navigation}>
<StatusBar backgroundColor="#28F1A6" />
</WeeklyAgenda >
</View>
);
}
}
WeeklyAgenda.propTypes = {
navigation: PropTypes.object.isRequired
}
export default AgendaScreen;
Я довольно новичок в своем роде и все еще пытаюсь понять, как делиться данными между компонентами и экранами.
2 ответа
Для этого вы хотите взять свои предметы и отобразить их в компоненте повестки дня. Я не знаю, какие реквизиты содержит объект item, поэтому я просто составил item.name и item.whothing. Я также не знал, как вы хотите отобразить эти данные. Вы можете отобразить его так, как вам нравится, в операторе return функции map.
Если вы хотите, чтобы таблица просто отображалась в операторе return и динамически добавляла реквизиты вашего элемента соответственно. Имеет ли это смысл?
Кроме того, самый внешний элемент вашего возврата в вашей функции карты должен иметь ключевую опору, которая принимает только уникальные ключи.
state = {
items: [],
selectedDate: ''
}
render() {
const { items } = this.state; // destructure state, so create a variable called items that is equal to this.state.items essentially takes the items in the state variable items and copies them to a new const variable called items
return (
<View style={{height:600}}>
<Agenda
items={items} //send those items to Agenda Component via prop
loadItemsForMonth={this.loadItems.bind(this)}
selected={this.props.day}
renderItem={this.renderItem.bind(this)}
renderEmptyData={this.renderEmptyDate.bind(this)}
rowHasChanged={this.rowHasChanged.bind(this)}
onRefresh = {() => { this.setState({refeshing : true})}}
refreshing = {this.state.refreshing}
refreshControl = {null}
pastScrollRange={1}
futureScrollRange = {3}
theme = {
{
agendaTodayColor: '#28F1A6',
agendaKnobColor: '#28F1A6',
dotColor: '#28F1A6',
selectedDayBackgroundColor: '#28F1A6',
todayTextColor: '#28F1A6',
}
}
/>
);
}
//On application loads, this will get the already saved data and set the state true when it's true.
componentDidMount() {
AsyncStorage.getItem("key").then((newItems) => {
//assuming JSON.parse returns an array of JSON objects, if not change
//items to items: {} the way you had it originally in state
this.setState({ items: JSON.parse(newItems) }) //add the items or data to state variable called items
});
}
}
// давайте представим, что это ваш компонент повестки дня
class Agenda extends Component {
state = {
items: this.props.items, //get the items that were passed above using items={item} and assign them to a state variable items here in the Agenda component
}
render() {
const { items } = this.state; //destructure state
return(
<div>
//put this where you want to render the data in the AgendaComponent you said you were using a Text Component so it would like this
items.map(item => {
return (
//assuming that item object comes with an id if not you must add a unique key so you can call a func that updates a count variable and returns it
<Text key={item.id}>{item.name} {item.date}</Text>
)
})
)
</div>
}
}
Помните. функция map почти как цикл for. Он будет перебирать каждый элемент в массиве и делать то, что есть в операторе возврата для каждого элемента массива.
Надеюсь это поможет.
Обновить
В предыдущем решении, которое я выписал, была ошибка. Компонент Agenda устанавливал состояние до того, как он фактически получил реквизиты, и не обновлял состояние при обновлении реквизитов.
По какой-то причине componentWillReceiveProps никогда не получал никаких реквизитов. Однако componentDidUpdate получил реквизиты после одного обновления. Проблема в том, что вы не можете setState
в componentDidUpdate()
или вы получите бесконечный цикл. Так что вот работа вокруг. Если у кого-то есть лучшее решение, пожалуйста, отредактируйте мой ответ или отправьте новый ответ с этим.
Следующее только для вашего компонента повестки дня. Больше ничего не нужно обновлять
Во-первых, обновите вашу декларацию состояния из этого:
state = {items: this.props.items, // получить элементы, которые были переданы выше, используя items={item} и назначить их переменным состояния элементов здесь, в компоненте Agenda}
к этому:
state = {
items: null, //changed this to null bc the array gave a nested array later on making it more difficult to traverse through
haveItems: false //this will be used later to check if there are items available in props to update state
};
В вашем
render()
функция изменить этоrender() { const { items } = this.state; //destruct state return( // поместите это туда, где вы хотите визуализировать данные в Agenda Component, который, как вы сказали, вы используете текстовый компонент, поэтому он хотел бы этот items.map(item => { return ( // при условии, что объект item приходит с ID, если нет, вы должны добавить уникальный ключ, чтобы вы могли вызвать функцию, которая обновляет переменную count и возвращает ее {item.name} {item.date}) })) } }
к этому:
const { items, haveItems } = this.state; //destructure state
if (this.props.items.length === 0) {
this.setState({ haveItems: false }); //if there aren't any items in props then you have no items so set haveItems to false
} else {
//if you already have items then you already handled those items so do nothing
if (haveItems) {
} else {
//handle the items
this.setState({
haveItems: true, //set this to true bc now you have items
items: this.props.items //set items in state to the items in props, you will get one array with all of your objects
});
}
}
return (
<div>
<div>
{haveItems
{* if you have items then map them the question mark means true, if not display nothing, the colon : means false
? items.map(item => {
return (
<ul key={item.id}>
<li>{item.name}</li>
<li>{item.date}</li>
</ul>
);
})
: null}
</div>
</div>
);
}
}
Извините за длинный ответ, но я надеюсь, что это то, что вам нужно.
Таким образом, в основном в потоке данных между компонентами используются реквизиты. В случае, если вам нужно передать все состояние одного компонента другому, вы можете передать реквизиты второму компоненту, сгруппировав состояние, и получить реквизит, проанализировав его снова для объекта json.
Если вы используете любую навигацию для перемещения между компонентами и экранами, вы также можете использовать passProps для отправки состояния на следующий экран. Надеюсь это поможет.