2

I don't know what happen to my code. I'm trying to create a Todo list. I'm using listview. I have two components the Todo and the AddTodo.

main component

import React, { Component } from 'react';
import { View, Text, TextInput, StyleSheet, ListView } from 'react-native';
import * as moment from 'moment';
import TodoList from '../compoents/TodoList';
import {TodoModel, ITodo} from '../models/TodoModel';

interface TodoProps{
    todo: TodoModel;
    ter:string;
}
interface TodoState{
    dataSource: any;
    myTodo: Array<ITodo>;
}


export default class Todo extends React.Component <TodoProps,TodoState>{

    constructor(props:TodoProps) {
        super(props);

        const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => true});
        this.state = {
            dataSource: ds,
            myTodo: []
        };

    }

    componentWillMount = () => {
        console.log(this.state.myTodo);
        let data = {
            title: this.props.ter
        };

        if (this.props.ter) {
          this.state.myTodo.push(data);
        }

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

    render() {

        return (
            <View style={{marginTop: 60}}>
                <ListView enableEmptySections={true} dataSource={this.state.dataSource} renderRow={(rowData) => <TodoList data={rowData} /> } />
            </View>
        )
    }
}

this will view the list of todo that I add from the form

AddTodo component

import * as React from "react";
import { Alert, View, Text, StyleSheet, TextInput, TouchableOpacity } from 'react-native';
import {TodoModel} from '../models/TodoModel';
import { Actions} from 'react-native-router-flux';
interface TodoState{
    todoText?: string;
}
interface TodoProps{
    text: string;
}
export default class AddTodo extends React.Component <TodoProps,TodoState> {

    constructor(props:TodoProps){
        super(props);
        this.state = {
            todoText:" "
        };
    }

    handleSubmit = () => {

        Actions.todo({ter: this.state.todoText});
    }

    render() {
        return (
            <View style={{margin: 128, marginLeft: 15, marginRight:15}}>
                <Text>ADD</Text>
                <TextInput autoCorrect={false} style={styles.input} placeholder='Todo' onChangeText={(text) => this.setState({todoText:text})} value={this.state.todoText} />
                <TouchableOpacity onPress={this.handleSubmit.bind(this)}>
                    <Text style={styles.button}>Submit</Text>
                </TouchableOpacity>
            </View>
        )
    }
}


const styles = StyleSheet.create({
    button: {
        backgroundColor: '#ccc',
        color: 'white',
        height: 40,
        lineHeight: 30,
        marginTop: 10,
        textAlign: 'center',
        alignSelf: 'stretch',
        borderRadius: 5,
        justifyContent: 'center',
        alignItems: 'center',
    },
    container: {

    },
    input: {
        borderColor: '#ededed',
        borderRadius: 5,
        borderWidth: 1,
        height: 37,
        alignSelf: 'stretch',
    }
})

the issue here. When the time I add one todo. I was successfully added to the myTodo array, but when I add the second todo. It removes the first todo, and show the second todo. It doesn't push and add to the array. Why it happen that way? but if you have tutorial on how to do it. It will more great to study for it. I'm very interested to learn react native.

update

export default class App extends React.Component<Props, State> {
    render() {
        return (
            <Router>
                <Scene key="root">
                        <Scene key="todo" component={Todo} title="Todo" initial={true}  onRight={() => Actions.addTodo({text: 'Hello World!'})}
                               rightTitle="Add"/>
                        <Scene key="addTodo" component={AddTodo} title="Add Todo" backTitle="Cancel" />
                </Scene>
            </Router>
        );
    }
}
user3818576
  • 2,979
  • 8
  • 37
  • 62
  • I'm pretty sure when you do Actions.scene(props) that scene is reset. So when you do `Actions.todo({ter: this.state.todoText});` in AddTodo you're basically resetting the todolist and passing the new text as the prop (i.e. you never saved or passed the old data so its gone). To get around this, I suggest you redesign addTodo to store all the data in an array or find a way to pass the previous data as props so addTodo can append the new data and pass it back? – Daniel Persaud May 16 '17 at 02:42
  • I see. passing array to array. I will going to work on this thanks – user3818576 May 16 '17 at 02:47

2 Answers2

0

Let try my code, hope it can help you. Your "push" will mutate the state directly and that could potentially lead to error prone code, even if you are "resetting" the state again afterwards

You can find more here: Correct modification of state arrays in ReactJS

Edited:

  • rewrite handleSubmit function

handleSubmit = () => {
        Actions.pop({refresh : 
          {ter:   this.state.todoText}
        });
    }
  • Change componentWillMount to componentWillReceiveProps

    componentWillReceiveProps = (nextProps) => {
        console.log(this.state.myTodo);
        let data = {
            title: nextProps.ter
        };
        let todoList = this.state.myTodo
        

        if (nextProps.ter) {
          todoList.push(data);
        }

        this.setState({
            myTodo: todoList
        });
    }
Community
  • 1
  • 1
Kawatare267
  • 4,168
  • 3
  • 16
  • 16
  • @user3818576 I had edited my answer, hope it will help you :D the problem was you had created a new scene with the new initial state. – Kawatare267 May 16 '17 at 03:34
  • thanks for this. It's working now. but i dont have an idea why you use Actions.pop and then componentWillReceiveProps() function – user3818576 May 16 '17 at 03:39
  • do you have link for documentation about the activity.pop and the componentWillReceiveProps? I'm trying to search it to google but I can't find solid info for this. thanks anyway – user3818576 May 16 '17 at 03:42
  • @user3818576 when you use Actions.yourSceneKey() you will add more scene to your Scene Stack. For Example: Todo -> AddToDo -> (new)ToDo. The (new) ToDo Scene doesn't have your state of your Todo Scene. So the correct Stack should be Todo-> AddTodo. – Kawatare267 May 16 '17 at 03:43
  • I use pop to go back to Todo Scene and then refresh it to receive the props – Kawatare267 May 16 '17 at 03:45
  • thanks for this. It is a big help for me to understand more. – user3818576 May 16 '17 at 03:51
  • @user3818576 You're welcome. The stack looks exactly this image [link](http://mcgivery.com/wp-content/uploads/2015/12/stack.png) – Kawatare267 May 16 '17 at 03:53
  • what is the use of refresh in Actions.pop({refresh : – user3818576 May 16 '17 at 10:20
  • @user3818576 it will pop to your previous scene and then do Actions.Refresh - update the properties of this screen – Kawatare267 May 16 '17 at 10:26
0

You can see my answer for a very similar issue:

React Native: Child ListView will not update after element in parent is changed

So in fact, in your case, you may simply do this:

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

export default class Todo extends React.Component <TodoProps,TodoState>{

    constructor(props:TodoProps) {
        super(props);   
        this.state = {
            dataSource: ds.cloneWithRows([]), // or put your initial data here
            myTodo: []
        };
    }

Then, every time you want to update the list, just use ds.cloneWithRows(NEW_DATA)

componentWillMount() {
    // ...

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

One more thing, for react's default functions in component's life cycle, they are bound automatically, so don't need to use componentWillMount = () => {}, just use componentWillMount()

This is just the way you should do, if errors still happen, please show here, then we may figure them out together, thanks

Community
  • 1
  • 1
thinhvo0108
  • 2,212
  • 1
  • 13
  • 23
  • I get the same issue of mine. I think when it submit then navigate to main component, the constructor function will initiate and do myTodo to reset the array – user3818576 May 16 '17 at 03:24
  • So actually, can you make sure that extends "React.Component works fine" ^^ ? How about just "extends React.Component" ? – thinhvo0108 May 16 '17 at 03:28
  • Yes it working fine. I'm thinking another way that will remove the code inside the constructor, so that it will not initiate again. – user3818576 May 16 '17 at 03:31