1

I have a list of country names and when clicked on one of the countries, the state of the picked country should be updated with the name of the newly selected country. This state then triggers other changes in a useEffect(). The state of pickedCountry is handled in a parent component, but the setPickedCountry() function is passed to the child component (which creates the country list) as a prop.

When I now add a onPress={props.setCountry.bind(this, country.name)} to each of the list items I am getting a warning stating:

Warning: State updates from the useState() and useReducer() Hooks don't support the second callback argument. To execute a side effect after rendering, declare it in the component body with useEffect().

Right now I don't know how a useEffect would help me here. Could somebody help me out?


These are my components:

Country Data

[
    {
        "name": "Germany",
        "data": [*SOME DATA*]
    },
    {
        "name": "France",
        "data": [*SOME DATA*]
    }, ...
]

Parent Component

const [pickedCountry, setPickedCountry] = useState(null);

useEffect(() => {
    if (pickedCountry!= null) {
      //Do Something
    }    
  }, [pickedCountry]);

return (
    <Child setPickedCountry={setPickedCountry} />
);

Child Component

const [child, setChild] = useState([]);

useEffect(() => {
    const myComponents= [];
    for (country of myData) {
        myComponents.push(
            <TouchableOpacity 
                onPress={props.setCountry.bind(this, country.name)} />
        )
    }
    setChild(myComponents);
}, [someProps];

return (
    {child.map((x) => x)}
)

1 Answer 1

2

Functional components are instanceless, therefore, there is no this to do any binding anyway. It looks as if you simply want to pass a country name as a new state value, i.e. onPress={() => props.setCountry(country.name)}.

useEffect(() => {
  const myComponents= [];
  for (country of myData) {
    myComponents.push(<TouchableOpacity onPress={() => props.setCountry(country.name)} />)
  }
  setChild(myComponents);
}, [someProps];

Or create a curried function handler so only a single handler is defined and passed. The country name as saved in the enclosure.

useEffect(() => {
  const myComponents= [];
  const onPressHandler = name => () => props.setCountry(name);
  for (country of myData) {
    myComponents.push(<TouchableOpacity onPress={onPressHandler(country.name)} />)
  }
  setChild(myComponents);
}, [someProps];
Sign up to request clarification or add additional context in comments.

4 Comments

Thx! Your second version worked for me. I actually initially used your first version, but my problem was that no matter which country I clicked on my list, the pickedCountry state was always set with the name of the last instance of the country list in my for loop. Do you know why that is? Because of that I changed to that binding strategy, which gave me the correct states, but with the stated warning.
@JoeBe Well, you've used the effect hook in a rather odd way (looks like you are trying to memoize some JSX (maybe for performance?)), and normally you'd just array::map your myData to a result array. Off the top of my head, no, I don't see why the first way wouldn't work. To dig any deeper I'd want to see a running codesandbox (or expo snack for RN) to examine what myData is and do some debugging. In any case though I like and prefer the second method.
Actually, it is a rather complex svg chart that is created there (I just used the list example for simplicity), so yes, it is a performance reason I guess. But based on the code I showed here, I am eager to know why you say "you've used the effect hook in a rather odd way"? And the for loop includes several other computations, which is why I go with a loop rather than a map() (personal preference ?)
@JoeBe Ah I see, I was basing my comments on the limited scope of the snippet you shared. It is generally considered an anti-pattern to store actual JSX in state, and there are other mechanisms for memoizing computed values and/or renderable JSX (i.e. useMemo, useCallback, memo HOC, etc...). If what you have is working though I wouldn't worry about it much now.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.