0

In my project, while looping through a datafile with the .map() function, I'm passing down a function (updatestate) from the Parent Component, App, to the Child, Main, that allows the Child to call .setState() and append to the array in the Parent state.

But, when I call this function to set Parent's state, the mapped section -- {contents} -- won't rerender unless I tap the screen again.

Any ideas? Am I missing some binding or need to do some componentDidMount or anything? I've tried adding this.forceUpdate() inside the eventHandler but the **mapped {contents} section ** still won't render automatically.

const RootStack = StackNavigator(
  {
    Main: {
      screen: Main}
  }
);

export default class App extends Component<{}> {
    constructor(props) {
      super(props);
      this.state = {
        favoritesList: []
      };
    }

  updateArr=(itemname, url)=>{this.setState({ favoritesList: [...this.state.favoritesList, {"item_name":itemname, "url":url}]})};

  render() {
      return <RootStack screenProps={{appstate: this.state,
                                    updatestate: this.updateArr}}
      />;
    }
  }

class Main extends React.Component {

  render() {
    var updatestate = this.props.screenProps.updatestate;

    //collection is the data (local JSON) i'm looping thru via .map in Main Component

    const contents = collection.map((item, index) => {
        return (
            <Card key={index}
                  onSwipedRight={() => {updatestate(item.item_name,item.url)}}
            >
              <View> //THIS and anything after <Card> doesn't render for the next card automatically if I do 'onSwipedRight'
                <Image
                    source={{uri: item.url}} />
               </View>
            </Card>
        )
      },this);

      return (
      <View>
            <CardStack>

              {contents} //THIS IS THE PART THAT WON'T AUTO-RERENDER AFTER THE EVENT HANDLER THAT SETS THE PARENT'S STATE

            </CardStack>
      </View>
        );
    }
}

To make sure it's not an issue with the event handler, I added in a function that doesn't set the state of the parent, and had <Card> call that on the event handler -- it works perfectly and the child component <Card> renders perfectly. It seems that it's the updatestate function passed down from the parent to the child that acts to call .setState() upstream that for some reason is causing the Child to not render/not finish rendering the mapped {contents} after the event handler.

class Main extends React.Component {
  render() {

    var updatestate = this.props.screenProps.updatestate;

    var newfunc = (a, b) => {console.log('a:', a, 'b:', b)};

      const contents = collection.map((item, index) => {
        return (
            <Card key={index}
                  newfunc(item.item_name,item.item_name);}}
                  // onSwipedRight={() => {updatestate(item.item_name,item.url); }}
                  >
SpicyClubSauce
  • 4,076
  • 13
  • 37
  • 62

2 Answers2

1

Try rewriting your setState like so

import { InteractionManager } from "react-native";

updateArr = (itemname, url) => {
  InteractionManager.runAfterInteractions(() => {
    this.setState( prevState => ({
      favoritesList: [...prevState.favoritesList, {       "item_name":itemname, "url":url } ]
    });
  });
  
}

// add this line to your parent component constructor()
this.updateArr = this.updateArr.bind(this);

In addition I suggest you change your React.Component to React.PureComponent instead it will handle the shallow equal of your state changes for you and increate performance. for reference React.Component vs React.PureComponent

  • :( still no luck with that suggestion -- parent's state is updating properly (as it was before as well), but after the event of `onSwipedRight` the mapped part, `{contents}` is not rendering. – SpicyClubSauce Feb 26 '18 at 05:14
  • I wonder if the animation of the swipe is not allowing the state to update properly. try wrapping the setState inside the updateArr with InteractionManager.runAfterInteractions(() => { this.setState( ... ) }) also in your App (parent component) add this line to the constructor this.updateArr = this. updateArr.bind(this); – Yaniv Shnaider Feb 26 '18 at 05:29
  • Ooh that sounds like it could potentially be it! I'm having some issues implementing your suggestions in your last comment though from this format -- could you add it onto your answer in code form? much appreciated! – SpicyClubSauce Feb 26 '18 at 05:33
0

Try not using index as key. Meaningful key values help the engine figure out what needs to be redrawn.

In your case, I'd suggest key={item.url} as a first go.

https://medium.com/@robinpokorny/index-as-a-key-is-an-anti-pattern-e0349aece318

Tom
  • 8,509
  • 7
  • 49
  • 78