0

In my project, I'm looping through cards in my Child Component, and updating the Parent's state with ones where I activate an event of 'swiping right' on the card to favorite. The parent's state keeps track of all favorites by adding the favorited card to the array.

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 activate the eventhandler onSwipedRight inside the Child Component, the parent's state gets updated as planned with the new Card, but nothing below the <Card> in Main gets rendered automatically for the next card, as it should. If I tap the screen, then the next card/picture renders only then.

Any ideas? Am I missing some binding or need to do some componentDidMount or anything so the code in child component, Main, renders even after I activate the event handler that updates the parent state?

Basically, is there a tool in React to make sure something renders or at least waits for it to render? (post event handling which sets the parents state in my case)

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

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;

    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}

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

(abbreviated) Project structure:

App
 |__ Rootstack
       |
       |__Main

UPDATE (more info): Just to test 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 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); }}
                  >
Simon Mourier
  • 132,049
  • 21
  • 248
  • 298
SpicyClubSauce
  • 4,076
  • 13
  • 37
  • 62

1 Answers1

0

If you need to set the state of a component based on the previous state, you should use

this.setState((prevState, props) => {})

Which should, in your case look like

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

For more on setState

h1b9b
  • 1,010
  • 5
  • 16
  • thanks! but what about the 2nd answer here on 'correct way of pushing into state array'? https://stackoverflow.com/questions/37435334/correct-way-to-push-into-state-array -- also, how would your solution eliminate the non-rendering-after-event-handling issue? – SpicyClubSauce Feb 25 '18 at 22:30
  • with destructuring, the state is still immutable it's not a dirty way if you want a more efficient way, personally I prefer working with {} than []. – h1b9b Feb 25 '18 at 22:37
  • as of the non-rendering-after-event-handling issue, as I didn't test your code I don't see why it's not updating. you could try destructuring `this.state` when passing it as a prop to `RootStack` to be sure it doesn't get mutated – h1b9b Feb 25 '18 at 22:47
  • what do you mean when you say destructuring? Could you give an example of what destructuring is? – SpicyClubSauce Feb 25 '18 at 22:50
  • also, thanks for the help and advice btw - but not sure it answers my question (added a section in bold to my original question up top that summarizes the real nature of my question) – SpicyClubSauce Feb 25 '18 at 22:55
  • maybe it's not the right term but I mean by destructuring assignments like `const a = { ...b }` as it will create a new object `a` by destructuring `b` – h1b9b Feb 25 '18 at 23:13
  • there is [forceUpdate](https://reactjs.org/docs/react-component.html#forceupdate) to force a React component to update, but normally you shouldn't use it if your design is following to react principles – h1b9b Feb 25 '18 at 23:16