0

I have 3 inputs and plus button when pressed it I want to add another 3 inputs so with his state,

the state like this

state={
 toolsUsed: [
      {
        id: (Math.random() * 150).toString(),
        name: '',
        price: '',
        count: '',
      },
     {..},
    ],
}

in the first 3 input component, I want when I add a change value text set the state with this value!

but I tried it but after changing the text I got in a state

toolsUsed => [" ","text"]

what I want to achieve

toolsUsed => [{name:'text",count:'..',price:'....',id:'..'},{...}]

UI

UI

here's my code

renderToolsUsed = () => {
   const {toolsUsed} = this.state;
   const prom = toolsUsed.map(({name, id, price, count}, i) => {
      return (
        <View>
          <View style={styles.toolsItem}>
            <TextInput
              onChangeText={name =>
                this.setState(
                  prevState => {
                    return {
                      ...prevState,
                      toolsUsed: [prevState.toolsUsed[i].name, name],
                    };
                  },
                  () => console.log(this.state.toolsUsed),
                )
              }
              value={name}
              key={id}
            />
          </View>


          <View style={styles.toolsItem}>
            <TextInput
              onChangeText={count => this.setState({})}
              value={count}
              key={id}
            />
          </View>

          <View style={styles.toolsItem}>
            <TextInput
              onChangeText={price => this.setState({})}
              value={price}
              key={id}
            />
          </View>
          <TouchableOpacity onPress={() =>
          this.setState(prevState => {
            return {
              ...prevState,
              toolsUsed: prevState.toolsUsed.length + 1,
            };
          })
        }> **// heres i got error "toolsUsed.map not a function**
            <Text>+</Text>
          </TouchableOpacity>
        </View>
      );
    });

     return prom;
   }

JSX

...
render(){
  return(
      <View style={styles.toolsContainer}>
            <Text>
              Tools
            </Text>
            {this.renderToolsUsed()}
        </View>
     );
 }

EDIT~1

maybe :D solve plus function

 onPress={() => {
                let newInput = this.state.toolsUsed.length;
                this.setState(prevState => ({
                  toolsUsed: prevState.toolsUsed.concat([newInput]),
                }));
         } 

EDIT ~ 2

I add a minus button to delete the last object in tools array, here two ways to handle it, but I have something weird on the first way it's work well I don't know why! but when I comment first var newInput it's don't delete the last item

onPress={() => {
        // first way
         let newInput = this.state.toolsUsed.pop();
         this.setState(prevState => ({
              toolsUsed: prevState.toolsUsed.filter(
                 tool => tool.id !== id,
               ),
         }));
       // Second
         let rowDeleted = [...this.state.toolsUsed];
         rowDeleted.splice(-1, 1);
         this.setState({toolsUsed: rowDeleted});
 }}
7
  • 1
    Using a random generator to produce a unique id is not a great idea. Commented Dec 19, 2019 at 15:56
  • @Kobe hmm, why? Commented Dec 19, 2019 at 15:58
  • Why would it be a good idea? If you depend on an id, then you're burying a bug, since it won't happen often, but it will happen, that is, generating the same id. Commented Dec 19, 2019 at 16:01
  • well, how can I generate id Commented Dec 19, 2019 at 16:02
  • 1
    By starting from 0 and work your way up? That's a common approach Commented Dec 19, 2019 at 16:03

1 Answer 1

1

You have to do something like this in the state update. It's not pretty, but your edit of the state is pretty deep.

This should edit the name of the i'th element in the toolsUsed property of your state:

onChangeText = {name =>
    this.setState(
        prevState => {
            return {
                toolsUsed: [
                    ...prevState.toolsUsed.slice(0, i),
                    {
                        ...prevState.toolsUsed[i],
                        name
                    },
                    ...prevState.toolsUsed.slice(i + 1)
                ],
            };
        },
        () => console.log(this.state.toolsUsed),
    )
}

Note that your TouchableOpacity onPress also breaks your state. It replaces the array with a number.

Your edited function still doesn't add a correct new entry to the array. It should add an object with the same structure. It should look something like this:

onPress={() => {
    let newInput = {id: generateId(), name: '', price: '', count: '',};
    this.setState(prevState => ({
        toolsUsed: [...prevState.toolsUsed, newInput],
    }));
}}

As you mentioned in the comments, the last fix missing was adding stable keys to each View in the mapped array, like this: <View key={i} >..</View>

Sign up to request clarification or add additional context in comments.

14 Comments

Thanks for the response, for setState It's work, but when I type just first character of a word the keyboard disappear and so on
I gave my code a bit more context. If you replace your onChangeText with this one, you still lose your array when editing the name?
I think the issue is i put map inside render() so in every re-render, i will lose it
The difference is that you were not adding a new object to the array, but a number, and that will lead you to problems when you do things like this.state.toolsUsed[this.state.toolsUsed.length - 1].name.
For the generateId issue, please create another stackoverflow question. This one is already getting pretty big, and out of scope.
|

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.