简体   繁体   English

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

[英]react-native: send complete STATE from one component to another?

I have a Reminder component comprising of a form where I am storing text and date onClick of a button using AsyncStorage . 我有一个Reminder component其中包含一个表单,其中使用AsyncStorage存储按钮的文本和日期onClick。

Now, I want to display this stored data in Agenda Component . 现在,我想在Agenda Component显示此存储的数据。

I am using Agenda component from react-native-calendars library react-native-calendars 我正在使用来自react-native-calendars库的Agenda component react-native-calendars

this is my reminder component 这是我的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;

and this it's screen Reminder screen 这是屏幕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;

and this is the component I want to display that data Agenda Component 这是我要显示该数据的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;

and this is it's screen Agenda Screen 这是屏幕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;

I am fairly new to react-native and still trying to figure out how to share data between components and screens. 我对本机响应还很陌生,但仍在尝试弄清楚如何在组件和屏幕之间共享数据。

项目结构

So basically in react-native data flow between components involves props. 因此,基本上,组件之间的本机数据流都涉及道具。 In case you need to pass the entire state of one component to another, you can pass props to the second component by stringifying the state and recieve the prop by parsing it to json object again. 如果需要将一个组件的整个状态传递给另一个组件,则可以通过对状态进行字符串化来将props传递给第二个组件,并通过再次将其解析为json对象来接收prop。

If you are using any navigation to navigate between components and screens, you can also use passProps to send state to next screen. 如果使用任何导航在组件和屏幕之间导航,则还可以使用passProps将状态发送到下一个屏幕。 Hope this helps. 希望这可以帮助。

To do this you want to take your items and map them in your Agenda component. 为此,您需要拿走物品并将其映射到“议程”组件中。 I don't know what props the item object contains so I just made up item.name and item.whatever. 我不知道item对象包含什么道具,所以我只是组成了item.name和item.whatever。 I also didn't know how you wanted to display this data. 我也不知道您想如何显示这些数据。 You can display it however you like in the return statement of the map function. 您可以根据需要在map函数的return语句中显示它。

If you want a table just have to render a table in the return statement and dynamically add your item props accordingly. 如果您想要一个表,只需在return语句中呈现一个表,并相应地动态添加您的道具。 Does this make sense? 这有意义吗?

Also, the outermost element of your return in your map function must have a key prop that accepts unique keys only. 同样,您在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
        });
    }
}

//let's pretend that this is your Agenda Component //让我们假装这是您的议程组件

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>
    }
}

remember. 记得。 the map function is almost like a for loop. map函数几乎就像一个for循环。 It will iterate through every element in the array and do what is in the return statement for each element of the array. 它将遍历数组中的每个元素,并对数组中的每个元素执行return语句中的操作。

Hope this helps. 希望这可以帮助。

Update 更新

The previous solution I wrote out had a bug. 我以前写的解决方案有一个错误。 The Agenda component was setting the state before it actually received props and did not update the state when props updated. 议程组件在实际接收道具之前设置状态,并且在道具更新时未更新状态。

For some reason, componentWillReceiveProps never got any props. 由于某些原因,componentWillReceiveProps从未获得任何道具。 However, componentDidUpdate received props after one update. 但是,componentDidUpdate在一次更新后收到了道具。 The issue is that you cannot setState in componentDidUpdate() or you will get an infinite loop. 问题是您不能componentDidUpdate() setState ,否则将发生无限循环。 So here is a work around. 所以这里是一个解决方法。 If anyone has a better solution to this please edit my answer or post a new answer with that. 如果有人对此有更好的解决方案,请编辑我的答案或发布新答案。

The following is only for your Agenda Component. 以下内容仅适用于您的议程组件。 Nothing else needs updating 没什么需要更新的了

  1. First, update your state declaration from this: 首先,从此更新您的状态声明:

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

to this: 对此:

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. In your render() function change this 在您的render()函数中更改此

    render() { const { items } = this.state; render(){const {items} = this.state; //destructure state return( //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 {item.name} {item.date} ) }) ) } } // destruct state return(//将其放在您要使用文本组件在AgendaComponent中呈现数据的位置,因此需要此items.map(item => {return(//假设该item对象来了如果没有ID,则必须添加一个唯一键,以便您可以调用一个函数来更新计数变量并返回它。{item.name} {item.date}}}))}}

to this: 对此:

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>
    );
  }
}

Sorry for a long answer but I hope this is what you needed. 对不起,很长的答案,但我希望这是您所需要的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM