react-native:将完整的STATE从一个组件发送到另一个组件?

时间:2018-12-08 15:36:21

标签: javascript reactjs react-native asyncstorage react-native-calendars

我有一个Reminder component,其中包含一个表单,其中我使用AsyncStorage来单击按钮时存储文本和日期。

现在,我想在Agenda Component中显示此存储的数据。

我正在使用Agenda componentreact-native-calendars中的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;

我对本机反应还很陌生,但仍在尝试弄清楚如何在组件和屏幕之间共享数据。

project stucture

2 个答案:

答案 0 :(得分:0)

因此,基本上,组件之间的本机数据流涉及道具。如果需要将一个组件的整个状态传递给另一组件,则可以通过对状态进行字符串化来将props传递给第二个组件,并通过将其再次解析为json对象来接收prop。

如果使用任何导航在组件和屏幕之间导航,则还可以使用passProps将状态发送到下一个屏幕。希望这会有所帮助。

答案 1 :(得分:0)

要执行此操作,您需要拿走物品并将其映射到Agenda组件中。我不知道item对象包含什么道具,所以我只是组成了item.name和item.whatever。我也不知道您想如何显示这些数据。您可以根据需要在map函数的return语句中显示它。

如果您想要一个表,只需要在return语句中呈现一个表并相应地动态添加您的道具即可。这有意义吗?

此外,您在map函数中返回的最外层元素必须具有仅接受唯一键的键属性。

 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循环。它将遍历数组中的每个元素,并对数组中的每个元素执行return语句中的操作。

希望这会有所帮助。

更新

我以前写的解决方案有一个错误。议程组件在实际接收道具之前设置状态,并且在道具更新时未更新状态。

由于某种原因,componentWillReceiveProps从未获得任何道具。但是,componentDidUpdate在一次更新后收到了道具。问题是您在setState不能 componentDidUpdate(),否则您将陷入无限循环。所以这里是一个解决方法。如果有人对此有更好的解决方案,请编辑我的答案或发布新答案。

以下内容仅适用于您的议程组件。没什么需要更新的

  1. 首先,从此更新状态声明:

    state = {             items:this.props.items,//使用item = {item}获取上面传递的项目,并将它们分配给Agenda组件中的状态变量items         }

对此:

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
  };
  1. 在您的render()函数中更改

    render(){             const {items} = this.state; //解构状态             返回(                                //将其放置在您要在AgendaComponent中呈现数据的位置,您说过您正在使用文本组件,因此它想要这样                 items.map(item => {                     返回(                       //假设item对象带有一个id(如果不是),则必须添加一个唯一键,以便可以调用一个可更新count变量并返回它的func                       {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>
    );
  }
}

很抱歉,答案很长,但我希望这是您所需要的。