1
 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
            />


   );

2 Answers 2

1

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)
});
Sign up to request clarification or add additional context in comments.

4 Comments

This make sense. But now there is no row rendered in list after first click. (Note:onAddTSPress is click listener of button) First item added in list after second click.
Keep datasource in state and update it by setState method, You are directly updating this.datasource, which is mutable and render don't know about this change.
Tried. But no luck. I have eidted my onAddTSPress function. You may have a look
I have updated my answer, Try to update datasource from new ds.
1

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>

3 Comments

Just replaced this.setState({ timesheet: this.state.timesheet.push(project) }); with this.setState(prevState => ({ timesheet: [...prevState.timesheet, project] })); I do not get anything in list on first click. First item added in list after second click and second after third click and so on
can you please add onAddTSPress() call
I have update my answer hope that should solve your problem of re-rendering

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.