Search code examples
javascriptreact-nativenative-base

how to set checked value in checkbox


In the following React-native code I have used native-base check box to functions to consider are _checkBox() and _checkbox_check() where i have used a state variable to hold the values whether they are checked or not in a state cb{}

on pressing the checkboxes i am able to change the values which i have checked using console.log() in _checkbox_check() function but the value for the same is not workinkg at checked={this.state.cb[data.id]} which means the true and false in object cb corresponding to the keys of checkboxes are there but when its trues the checkboxes are checked, while i have checked giving static values like checked={true} which is working all fine.

please suggest if there is some mistake or chances of improvements code for my whole page is below

import React from "react";
import AsyncStorage from "@react-native-community/async-storage";
import {
   Text,
   View,
   ScrollView,
   Platform,
   Image,
   TouchableOpacity,
   ToastAndroid,
   StatusBar
   } from "react-native";

     import {
       Content,
       Container,
       Button,
       Icon,
       Header,
       Left,
       Right,
       Body,
       Title,
       Item,
       Card,
       CardItem,
       DatePicker,
       Label,
       Input,
       ListItem,
       CheckBox
     } from "native-base";

     import Network from "../../network/network";
     import LinearGradient from "react-native-linear-gradient";
     import * as constant from "../../Constants/Constant";
     const deviceHeight = constant.deviceHeight;
     const deviceWidth = constant.deviceWidth;
     import styles from "./Styles";
     import Styles from "./Styles";

     const icn = "../../Images/icons/blue/";
     const platform = Platform.OS;

     export default class SignInScreen extends React.Component {
       constructor(props) {
         super(props);
         this.state = {
           services: null,
           poolId: null,
           chosenDate: new Date(),
           choice: null,
           description: null,
           val: true,
           cb: {}
         };

         this.setDate = this.setDate.bind(this);
       }
       setDate(newDate) {
         this.setState({ chosenDate: newDate });
       }

       async componentWillMount() {
         await Network.Token_get("apk/getServiceList", async response => {
           if (response.status == true) {
             await this.setState({
               services: response.serviceData
             });
           }
         });
       }
       componentDidMount() {
         const { navigation } = this.props;
         const itemId = navigation.getParam("id");
         this.setState({
           poolId: itemId
         });
       }
       render() {
         return (
           <Container>
             <Header
               style={{
                 borderBottomWidth: 0,
                justifyContent: "center",
                 alignItem: "center"
               }}
             >
               <Left>
                 <Button
                      transparent
                   onPress={() => {
                     this.props.navigation.openDrawer();
                   }}
                 >
                   <Icon name="menu" />
                 </Button>
               </Left>
               <Body>
                 <Title style={styles.title}>Service Details</Title>
               </Body>
               <Right>
                 <Button transparent onPress={() => this._signOutAsync()}>
                   <Icon name="log-out" />
                 </Button>
               </Right>
             </Header>
             <Content contentContainerStyle={styles.content} bounces={false}>
               <ScrollView
                 contentContainerStyle={{
                   padding: 10,
                   alignItems: "flex-start",
                   justifyContent: "center",
                   backgroundColor: "#ffff",
                   shadowColor: "#1C0C59",
                   shadowOffsetWidth: 3,
                   shadowOffsetHeight: 3,
                   shadowOpacity: 0.1,
                   shadowRadius: 3,
                   elevation: 3,
                   borderRadius: 7,
                   bgColor: "#ffffff"
                 }}
               >
                 <View
                   style={{ alignItems: "flex-start", justifyContent: "flex-start" }}
                 >
                   {this._checkBoxes()}
                 </View>

                 <View style={{ flexDirection: "row" }}>
                   <View>
                     <Text style={{ top: 10 }}>
                       Schedule Date:
                       {/* {this.state.chosenDate.toString().substr(4, 12)} */}
                     </Text>
                   </View>
                   <View>
                     <DatePicker
                       defaultDate={new Date(2018, 4, 4)}
                       minimumDate={new Date(2018, 1, 1)}
                       maximumDate={new Date(2018, 12, 31)}
                       locale={"en"}
                       timeZoneOffsetInMinutes={undefined}
                       modalTransparent={false}
                       animationType={"fade"}
                       androidMode={"default"}
                       placeHolderText="Select Schedule Date"
                       // textStyle={{ color: "green" }}
                       placeHolderTextStyle={{ color: "#d3d3d3" }}
                       onDateChange={this.setDate}
                       disabled={false}
                     />
                   </View>
                 </View>

                 <Item floatingLabel style={{ marginTop: 20, marginBottom: 50 }}>
                  <Label>Description</Label>
                  <Input
                     multiline={true}
                     //  style={{height:200, textColor: "#000"}}
                     style={{ color: "#000", height: 100 }}
                     onChangeText={description => this.setState({ description })}
                   />
                 </Item>

                 <Button
                   rounded
                   primary
                   onPress={() => this._submit()}
                   style={{ paddingLeft: 18, alignSelf: "center" }}
                 >
                   <Text style={{ fontWeight: "600", color: "#000"          }}>Submit</Text>
                   <Icon name="send" />
                 </Button>
               </ScrollView>
             </Content>
           </Container>
         );
       }

       mapPools(poolList) {
         if (poolList) {
           return poolList.map(data => {
             return (
               <Card style={{ borderRadius: 7 }}>
                 <CardItem style={{ borderRadius: 7 }}>
                   <TouchableOpacity>
                     <View style={styles.View_inside_card}>
                       <View style={styles.Incard_icon_View}>
                         <Image
                           source={require(icn + "swimmingPool.png")}
                           style={styles.Incard_icon}
                         />
                       </View>
                       <View style={styles.Incard_Detail_View}>
                         <Text style={styles.Detail_name}>{data.pool_title}</Text>
                         <Text style={styles.Detail_name}>{data.address}</Text>
                       </View>
                     </View>
                   </TouchableOpacity>
                 </CardItem>
               </Card>
             );
           });
         }
       }

       _signOutAsync = async () => {
         var loginid = await AsyncStorage.getItem("@LoginID");
         payload = {
           loginId: loginid
         };
         await Network.Token_put("apk/logout", payload, async response => {
           ToastAndroid.show(response.message, ToastAndroid.SHORT);
           if (response.status === true) {
             await AsyncStorage.clear();
             this.props.navigation.navigate("Auth");
           }
         });
         // await AsyncStorage.clear();
         // this.props.navigation.navigate("Auth");
       };




       _checkBoxes = () => {
         if (this.state.services) {
           return this.state.services.map(data => {
             return (
               <ListItem noBorder>
                 <CheckBox
                   checked={this.state.cb[data.id]}
                   onPress={() => this._chkbox_check(data.id)}
                 />
                 <Text style={{ marginLeft: 20 }}>{data.services_title}</Text>
               </ListItem>
             );
           });
         }
       };

       _chkbox_check = param => {
         console.log(param, this.state.cb);

         if (this.state.cb.hasOwnProperty(param)) {
           var element = this.state.cb[param];
           console.log(element);
           this.state.cb[param] = !element;
           element = this.state.cb[param];
           console.log(element);
         } else {
           this.state.cb[param] = true;
         }
       };

       _submit = async sd => {
         var payload = {
           pool_id: this.state.poolId,
           request_schedule_dateTime: this.state.chosenDate,
           request_details: this.state.description,
           service_type_id: this.state.choice
         };
         var url = "apk/createPoolRequest";

         console.log("payload=>", payload);

         await Network.Token_post(url, payload, async response => {
           ToastAndroid.show(response.message, ToastAndroid.SHORT);
           if (response.status === true) {
             this.props.navigation.pop();
           }
         });
       };
     }

Solution

  • You should use setState when modifying a component's state:

    _chkbox_check = param => {
      if (this.state.cb.hasOwnProperty(param)) {
        var element = this.state.cb[param];
        this.setState({
          cb: {
            ...this.state.cb,
            [param]: !element,
          },
        });
      } else {
        this.setState({
          cb: {
            ...this.state.cb,
            [param]: true,
          },
        });
      }
    }
    

    According to the docs, modifying the state directly is a big no no. There are several reasons why, but the issue you're running in to is because of the fact that render() will not be triggered until after setState() is called.

    In the code above setState will only modify the cb state, but since you don't want to completely overwrite cb (since it stores the checked states for multiple checkboxes), you have to merge with the spread operator ..., and access the dynamic key by wrapping it in square brackets [].

    Here's a working example stripped of all the irrelevant code:

    import React from 'react';
    import { View } from 'react-native';
    import { ListItem, CheckBox } from 'native-base';
    
    export default class SignInScreen extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          cb: {}
        };
      }
    
      render() {
        return (
          <View>
            {this._checkBoxes()}
          </View>
        );
      }
    
      _checkBoxes = () => {
        const services = [
          { id: 'service1' },
          { id: 'service2' },
          { id: 'service3' },
        ];
        return services.map(data => {
          return (
            <ListItem noBorder>
              <CheckBox
                checked={this.state.cb[data.id]}
                onPress={() => this._chkbox_check(data.id)}
              />
            </ListItem>
          );
        });
      }
    
      _chkbox_check = param => {
        if (this.state.cb.hasOwnProperty(param)) {
          var element = this.state.cb[param];
          this.setState({
            cb: {
              ...this.state.cb,
              [param]: !element,
            },
          });
        } else {
          this.setState({
            cb: {
              ...this.state.cb,
              [param]: true,
            },
          });
        }
      }
    }
    

    If you omit the line with ...this.state.cb in setState, then cb will be completely overwritten with the new value.

    For example, say you start with:

    state.cb = { service1: true }
    

    And then call _chkbox_check('service2');, you'll get:

    state.cb = { service2: true }
    

    But with the spread operator you'll then get:

    state.cb = { service1: true, service2: true }
    

    SIDE NOTE

    Your _chkbox_check function can be simplified by not checking for the property in the state:

    _chkbox_check = param => {
      this.setState({
        cb: {
          ...this.state.cb,
          [param]: !this.state.cb[param],
        },
      });
    }
    

    This is because JS uses truthy values, so undefined will be interpreted as false.