lost9123193
lost9123193

Reputation: 11030

Passing functions and variables as arguments to map() in Javascript/React

I want to pass a prop called verified through each map function and I am having difficulty setting it up.

UPDATE:

Passing verified to renderContinents works, but when add a parameter to renderCountry like this:

  {continent.countries.map(renderCountry(null, verified))}

My output is blank. Shouldn't this work though?

Updated code:

  const renderCities = cities => {
    return (
      <div>
        <label>
          <input
              onChange={onChange}
              type="checkbox"/>
        {cities.name}
        </label>
      </div>
    );
  };

  const renderCountries = ({country, verified}) => {
    console.log("came to country");
    return (
      <div className="city-location"> 
        <label>
            <input
              onChange={onChange}
              type="checkbox"/>
          {country.name}
        </label>
        {country.cities.map(renderCities)}
      </div>
    ); 
  };



function onChange(e) {
  console.log('checkbox verified:', (e.target.verified));
}


class AreasComponent extends Component {
      constructor(props) {
        super(props);
        this.state = { 
        };
        this.renderContinents = this.renderContinents.bind(this);
    }

  componentWillMount() {
    this.props.fetchAllAreas(); 
  }

  renderContinents(verified, continent) {
    console.log("came to continent");
      return(
        <div className="continent-location">
          <label>
            <input
              onChange={onChange}
                type="checkbox"/>
             {continent.name}
          </label>

          {continent.countries.map(renderCountries(null, verified))}

        </div>
    )
  } 


  render() { 
    if (!this.props.verified || !this.props.areas) {
      return <div>Loading Areas...</div>
    }
    return(
        <div> 
          {this.props.areas.map(this.renderContinents.bind(this, this.props.verified))}
        </div>
      );
    }
}

function mapDispatchToProps(dispatch){
  return bindActionCreators({ fetchAllAreas, checkArea}, dispatch);
}


function mapStateToProps(state) {
  return { areas: state.areas.all,
           verified:state.areas.verified
  };
}


export default connect(mapStateToProps, mapDispatchToProps)(AreasComponent);

My other problem is the onChange(e) function. It's global so it works when I click any checkbox, but I want to make it so that when onChange is clicked, it can take in a parameter and dispatch the action checkArea, which, to me means it has to be bound and should also be fed as a parameter. I tried this:

  {this.props.areas.map(this.renderContinents.bind(this, this.props.verified, this.props.checkArea))} 

but it returns a blank result. Is it possible to send a function into a map () parameter and is there a way to get renderCountry/renderCity to work with parameters?

Upvotes: 4

Views: 5072

Answers (2)

swen
swen

Reputation: 341

In fact, the simplest way for renderCountry/renderCity to call onChange() with checkArea action is to put them inside AreasComponent (i.e. as member functions). So they can access both onChange and checkArea.

class AreasComponent extends Component {
    constructor(props) {
        super(props);
        this.state = {};
        this.onChange = this.onChange.bind(this);
    }

    componentWillMount() {
        this.props.fetchAllAreas();
    }

    onChange(e, type) {
        console.log('checkbox verified:', this.props.verified);
        // call checkArea() here with your parameters
        this.props.checkArea(type);
    }

    renderCities(cities) {
        return (
            <div>
                <label>
                    <input
                        onChange={e => this.onChange(e, 'city')}
                        type="checkbox"/>
                    {cities.name}
                </label>
            </div>
        );
    };

    renderCountries(country) {
        console.log("came to country");
        return (
            <div className="city-location">
                <label>
                    <input
                        onChange={e => this.onChange(e, 'country')}
                        type="checkbox"/>
                    {country.name}
                </label>
                {
                    country.cities.map(this.renderCities)
                }
            </div>
        );
    };

    renderContinents(continent) {
        console.log("came to continent");
        return(
            <div className="continent-location">
                <label>
                    <input
                        onChange={e => this.onChange(e, 'continent')}
                        type="checkbox"/>
                    {continent.name}
                </label>
                {
                    continent.countries.map(this.renderCountries)
                }
            </div>
        )
    }

    render() {
        if (!this.props.verified || !this.props.areas) {
            return <div>Loading Areas...</div>
        }
        return(
            <div>
                {
                    this.props.areas.map(this.renderContinents)
                }
            </div>
        );
    }
}

function mapDispatchToProps(dispatch){
    return bindActionCreators({ fetchAllAreas, checkArea}, dispatch);
}


function mapStateToProps(state) {
    return {
        areas: state.areas.all,
        verified: state.areas.verified
    };
}


export default connect(mapStateToProps, mapDispatchToProps)(AreasComponent);

Upvotes: 1

Felix Kling
Felix Kling

Reputation: 816312

When you .bind parameters, those parameters become the first value(s) passed to the function. You should have noticed that when looking at the console.log output.

I.e. when you do

var bar = foo.bind(this, x, y);
bar(z);

you get the values in this order:

function foo(x, y, z) {}

You have switch the order of parameters in your function:

renderContinent(checked, continent) {
  // ...
}

However, you can just keep the code you have. You don't have to pass the value to renderContinents.

In order to pass it to renderContinents etc, either .bind it or put the call inside another function:

continent.countries.map(renderCountries.bind(null, verified))
// or
continent.countries.map(country => renderCountries(country, verified))

Upvotes: 5

Related Questions