Vinay
Vinay

Reputation: 2339

React: How to change status of toggle switch inside map?

I am trying to map a collection of keys to a list of toggle <Switch> components. The state of each toggle switch is coming from redux. When I click on the <Switch>, I expect the toggle state of the component to alternate.

I tried to call setState in the onChange handler on the <Switch>, but its not working as expected. How can I write a function that will toggle the switch?

This is what I have so far:

{Object.keys(all_product).map((item, index) => {
  console.log(all_product[item]);
  let product = all_product[item];
  this.state.activevalue = product.is_active;
  let val;

  product.is_active == 1 ? (val = true) : (val = false);

  this.state = { checked: val };

  return (
    <tr>
      <th scope="row">{product.id}</th>
      <td>
        <img
          src={`/img/${product.code}.jpg`}
          alt={product.name}
          class="img-fluid dataTableImg"
        />
      </td>
      <td>{product.name}</td>
      <td>{product.brand}</td>
      <td>{product.quantity}</td>
      <td>{product.unit_price}</td>
      <td>
        <Switch
          onChange={() => {
            this.setState({ checked: !val });
          }}
          checked={this.state.checked}
          value={"A"}
        />
      </td>
    </tr>
  );
})}

Upvotes: 3

Views: 6709

Answers (2)

Ajay Gaur
Ajay Gaur

Reputation: 5270

In your handleChange of Switch, pass the index/id in your callback as well. see this

import React from "react";
import Switch from "react-switch";
import update from "react-update";

class App extends React.Component {
  constructor(props) {
    super(props);

    //Assuming you've state in your component, that might as well be your redux state
    this.state = {
      all_products: [{ isActive: false, id: 1 }, { isActive: true, id: 2 }]
    };
  }
  handleChange = (activeStatus, itemId) => {
    const productIndex = this.state.all_products.findIndex(function(
      item,
      index
    ) {
      return item.id === itemId;
    });

    let products = [...this.state.all_products];
    let product = { ...products[productIndex] };
    product.isActive = activeStatus;
    products[productIndex] = product;

    //fire a redux action if all_products is stored in redux state
    this.setState({ all_products: products });
  };

  render() {
    const that = this;
    console.log(this.state.all_products);
    return (
      <div>
        {this.state.all_products.map(function(item, index) {
          return (
            <Switch
              checked={item.isActive}
              onChange={isActive => that.handleChange(isActive, item.id)}
            />
          );
        })}
      </div>
    );
  }
}

export default App;

Upvotes: 2

Dacre Denny
Dacre Denny

Reputation: 30360

If I understand your question correctly, you're getting the all_product map from a redux store. The all_products map contains an is_active field on values to determine the initial state of each <Switch> component and from there, the <Switch> is then controlled by state internal to the enclosing component.

Assuming my understanding is correct, one solution might be to read and update the checked prop of each <Switch> component via key/values tracked in the enclosing component:

/* Read checked state */
let isChecked = this.state[ item ]

/* Update (toggle) checked state */
this.setState({ [ item ] : !isChecked })

Integrating that with your code could look like this:

{Object.keys(all_product).map((item, index) => {

  /* Get product */
  const product = all_product[item];

  /* First try to get "checked value" from state */
  let isChecked = this.state[ item ]

  /* If isChecked fetched from state not present or
     then default to the is_active value supplied by
     your redux store */
  if(typeof isChecked === 'undefined') {
      isChecked = product.is_active;
  }

  return (
    <tr>
      <th scope="row">{product.id}</th>
      <td>
        <img
          src={`/img/${product.code}.jpg`}
          alt={product.name}
          class="img-fluid dataTableImg"
        />
      </td>
      <td>{product.name}</td>
      <td>{product.brand}</td>
      <td>{product.quantity}</td>
      <td>{product.unit_price}</td>
      <td>
        { /* Use locally defined isChecked to set checked 
        prop, and update setState to set negated isChecked
        for item by key  */}
        <Switch
          onChange={() => {
            this.setState({ [ item ]: !isChecked });
          }}
          checked={ isChecked }
          value={"A"}
        />
      </td>
    </tr>
  );
})}

Upvotes: 3

Related Questions