简体   繁体   中英

Child Component Not Getting New Data When Redux Updated By Parent Component

I have looked at other questions that seemingly had a similar issue, but none of the accepted answers have solved my issue. I am attempting to fetch new names and load them into child component when redux is updated with new IDs.

When I use only redux and no state (as I would prefer), the new IDs do not get passed along to the child component and the names do not load at all

Alternatively, I have tried using state for the names in the child component (as you can see in the commented text below). However ... Oddly enough, every time the IDs are changed, the component loads the names based on the previous IDs rather than the current IDs.

Redux

const initialState = {
  objectOfIds: {"someID":"someID", "aDifferentID":"aDifferentID"},  // I know this format may seem redundant and odd, but I have to keep it this way
  arrayOfNames: ["John Doe", "Jenny Smith"]
}

Parent Compoenent

// React
import React from 'react';
import firebase from 'firebase';

//  Components
import ListOfNames from './ListOfNames';

//  Redux
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import {set} from './../actions/index.js';

class ParentComponent extends React.Component {
  constructor(props) {
    super(props);
    this.changeIDs = this.changeIDs.bind(this);
  }

  changeIDs() {
    this.props.set("objectOfIds",{"aNewID":"aNewID","someOtherID":"someOtherID","anotherID":"anotherID"});
  }

  render (
    return (
      <div>
        <h2>Parent Component</h2>
        <button onClick={this.changeIDs}>Change Data</button>
        <ListOfNames objectOfIds={this.props.reduxData.objectOfIds}/>
      </div>
    )

  )
}

function mapStateToProps(state) {
    return {
        reduxData: state.reduxData
    };
}

function matchDispatchToProps(dispatch) {
    return bindActionCreators({
      set: set
    }, dispatch)
}

export default connect(mapStateToProps, matchDispatchToProps)(ParentComponent);

Child Compoenent

// React
import React from 'react';
import firebase from 'firebase';

//  Redux
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import {set} from './../actions/index.js';

// Firebase Database
var databaseRef;

class ListOfNames extends React.Component {
  constructor(props) {
    super(props);
    this.state= {
      arrayOfNames: []
    }
    this.fetchNamesForIds = this.fetchNamesForIds.bind(this);
    this.add = this.add.bind(this);
  }

  componentDidMount() {
    console.log("componentDidMount triggering...");
    firebase.auth().onAuthStateChanged(function (user) {
      if (!user) {
        console.log("no user authenticated");
      }
      databaseRef = firebase.database().ref('/people/' + user.uid);
      this.fetchNamesForIds(this.props.reduxData.objectOfIds);
    })
  }

  // I have tried making the fetch in componentWillReceiveProps so that the function would run anytime the IDs were updated in redux, but "this.props.objectOfIds" and "this.props.reduxData.objectOfIds"
  componentWillReceiveProps() {
    console.log("componentWillReceiveProps triggering...");
    console.log("this.props.objectOfIds");
    console.log(this.props.objectOfIds);
    console.log("this.props.reduxData.objectOfIds");
    console.log(this.props.reduxData.objectOfIds);
    this.fetchNamesForIds(this.props.reduxData.objectOfIds);
    // Note: I have also tried: this.fetchNamesForIds(this.props.objectOfIds); so that the data is passed in from the parent
  }

  // fetched the names for the associated IDs
  fetchNamesForIds(personIds) {
    if (personIds === [] || personIds === undefined || personIds === null) {

ALTERNATIVE TO LINE ABOVE

I would prefer to store the data in redux so that it is accessible to other components, but doing this did allow the data to load, but it loads with a lag (ie when I change the IDs, it loads the names associated to the previous IDs)

      // this.setState({
      //   arrayOfNames: []
      // });
      this.props.set("arrayOfNames", []);
      return
    }
    var arrayOfNames = [];
    // loop through person and set each value into the arrayOfNames array
    Object.keys(IDs).map(function(person, index) {
      console.log("person = " + person);
      console.log("index = " + index);
      // get names associated with the ids obtained
      var name = ''
      databaseRef.child('people').child(person).limitToFirst(1).on("value", function(snapshot) {
        var firstName = snapshot.child('firstName').val()
        var lastName = snapshot.child('firstName').val()
        name = firstName + " " + lastName
        console.log("name = " + name);
        arrayOfNames.push(name);
        console.log("arrayOfNames = " + arrayOfNames);
        this.props.set("arrayOfNames", arrayOfNames);

ALTERNATIVE TO LINE ABOVE

I would prefer to store the data in redux so that it is accessible to other components, but doing this did allow the data to load, but it loads with a lag (ie when I change the IDs, it loads the names associated to the previous IDs)

        // this.setState({
        //   arrayOfNames: arrayOfNames
        // });
      }.bind(this));
    }.bind(this));
  }

  render() {
    return(
      (this.props.user.arrayOfNames === [] || this.props.user.arrayOfNames === undefined || this.props.user.arrayOfNames === null || this.props.user.arrayOfNames.length < 1)
        ? <span>no people selected</span>
        : <div>
            <h5>List of People</h5>
            {this.props.user.arrayOfNames.map((name, index) => {
              return (
                  <h5>{name}</h5>
              )
            })}
          </div>
    )
  }
}

ListOfNames.propsTypes = {
  objectOfIds: React.PropTypes.Object
};


function mapStateToProps(state) {
    return {
        reduxData: state.reduxData
    };
}

function matchDispatchToProps(dispatch) {
    return bindActionCreators({
      set: set
    }, dispatch)
}

export default connect(mapStateToProps, matchDispatchToProps)(ListOfNames);

Similar Questions:

Does anyone understand how I can get my component to load the data based on the current IDs in redux?

Probably because the object keys are changed but not the object reference. A hacky solution would be to call this.forceUpdate() to update the component after the change:)

Tip: you don't need to bind your functions if you use the new javascript syntax.

this.add = this.add.bind(this); will be solved by writting the add method like:

add = () => {
};

I had a similar issue where I was loading a child component multiple times on one page and despite passing in what I thought was a unique ID it would only reference the first ID. I know this isn't exactly the situation you have but this will allow you to have a unique object key AND a unique object reference which will hopefully fix your issue.

This is the package I used for this: https://www.npmjs.com/package/react-html-id . When you import the package you need to have curly brackets.

import { enableUniqueIds } from 'react-html-id'

The rest is explained on npm.

The issue is the Child Component componentWillReceiveProps. You are not using the new props that are propagated to this component. componentWillReceiveProps is called with nextProps, which contains the updated props.

Use this in your child component

componentWillReceiveProps(nextProps) {
    console.log("componentWillReceiveProps triggering...");
    console.log("nextProps.objectOfIds ", nextProps.objectOfIds);
    console.log("nextProps.reduxData.objectOfIds ", nextProps.reduxData.objectOfIds);
    this.fetchNamesForIds(nextProps.reduxData.objectOfIds);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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