Najeeb Idrees
Najeeb Idrees

Reputation: 453

Add and delete row in React Native listView dynamically

 class CreateTS extends Component {

constructor(props) {
    super(props);

    this.state = {
        project: 'Select Project',
        activity: 'Select Activity',
        hour: '',
        task: '',
        timesheet: [],
        dataSource: []
    };

    const ds = new ListView.DataSource({
        rowHasChanged: (r1, r2) => r1 !== r2
    });

    this.state.dataSource = ds.cloneWithRows(this.state.timesheet);

    console.log('Here at constructo!');
}


onAddTSPress() {
    const { project, activity, task, hour } = this.state;

    console.log(project);
    console.log(activity);
    console.log(task);
    console.log(hour);


    this.setState({
        timesheet: this.state.timesheet.push(project)
    });

    console.log('Here button press!');

    const ds = new ListView.DataSource({
        rowHasChanged: (r1, r2) => r1 !== r2
    });

    this.dataSource = ds.cloneWithRows(this.state.timesheet);
}

renderRow(data) {
    console.log('Here at render row!');
    console.log(`${data} $""`);
    return <Text> {data} </Text>;
}

render() {
  return(

     Some input components that set state of project and activity with 
     values...
     A button that call onAddTSPress function.
     And a listview in which items will be add that we created above
  );
}

After running above code and add one item i got following screen which is expected behavior. enter image description here

But when i try to add another item in list i got following error. What is the issue? enter image description here

it saying push is not a function while first time it run successfully as expected. But second time it gives error and i am unable to add second item in list.

Here is my updated onAddTSPress function

   //Modified onAddTSPress function
   onAddTSPress() {
    const { project, activity, task, hour } = this.state;

    console.log(project);
    console.log(activity);
    console.log(task);
    console.log(hour);

    console.log(`${this.state.timesheet}`);
    this.setState({
        timesheet: [...this.state.timesheet, project]
    });

    console.log('Here button press!');
    console.log(`${this.state.timesheet}`);

    this.setState({ dataSource: 
    this.state.dataSource.cloneWithRows(this.state.timesheet) });
 }

 return (
   <Button
                onPress={this.onAddTSPress.bind(this)}
                btnStyle={styles.btnStyle}
                txtStyle={styles.txtStyle}
            >
                Add
            </Button>
            <ListView
                style={styles.listViewContianer}
                dataSource={this.state.dataSource}
                renderRow={this.renderRow}
                enableEmptySections
            />


   );

Upvotes: 1

Views: 2569

Answers (2)

Kishan Mundha
Kishan Mundha

Reputation: 3141

You are calling this.state.timesheet.push(project), push prototype of array return number of record in array after pushed. push method not returning latest array and you are setting state with return value of push method which are a number. First time it work fine because you initialize timesheet as array ([]) and first time push method work. But second time when you try to call push method of timesheet, which is now a number, it will throw array.

Solution

You can try [...this.state.timesheet, project] instance of push. By this way you will get updated array.

this.setState({
    timesheet: [...this.state.timesheet, project]
});

or

You can keep old value in a variable and push new value and reassign array to setState.

const timesheet = this.state.timesheet;
timesheet.push(project);
this.setState({
    timesheet: timesheet
});

Also use datasource inside state and update it by setState

const ds = new ListView.DataSource({
    rowHasChanged: (r1, r2) => r1 !== r2
});

this.setState({
    dataSource: ds.cloneWithRows(this.state.timesheet)
});

Upvotes: 1

Jigar Shah
Jigar Shah

Reputation: 6223

You should never mutate state directly, instead use following syntax

this.setState(prevState => ({
  timesheet: [...prevState.timesheet, project]
}))

For your comment:

First item added in list after second click and second after third click and so on

You need to re-render ListView on state change. To do so you can either add key like

key="this.state.listChanged" // your list state name 

or

(this.state.listChanged)
  ? <ListView />{/* or call your method */}
  : <Text>{/*some message */}</Text>

Upvotes: 1

Related Questions