1

I have a component which is address of company. Each address is an object which is consists of two fields address and metro. (I get rid of some code with declaring Props and State because it doesn't matter)

class CompanyAddress extends React.Component<Props, State> {
    constructor(props:Props) {
        super(props);
        this.state = {address:''}
    }

    changeAddress = (text:string) => {
        this.setState({address: text});
        this.props.changeAddress(this.props.index, text, this.state.metro);
    }

    changeMetro = (metro:{id: Number, name: string}) => {
        this.setState({metro:metro});
        this.props.changeAddress(this.props.index, this.state.address, metro.id);
    }

    render() {
        return (
            <View style={styles.container}>
                <TextInput
                    autoCompleteType="name"
                    placeholder={("address " + (this.props.num))}
                    maxLength={50}
                    style={[styles.address, {marginTop: 5}]}
                    onChangeText={(text) => { this.changeAddress(text)}}
                />

                <TouchableOpacity
                    style={styles.address}
                    onPress={() => this.props.navigation.navigate('MetroList', {changeMetro: this.changeMetro})}
                >
                    {this.state.metro ? (
                        <Text style={[styles.metroText, {color: 'black'}]}>
                            {this.state.metro.name}
                        </Text>
                    ) : (
                        <Text style={styles.metroText}>
                            Nearest metro
                        </Text>
                    )}
                </TouchableOpacity>

                {this.props.num != 1 ? (
                    <AntDesign
                        onPress={() => {this.props.removeAddress(this.props.index)}}
                        style={styles.minus}
                        name="minus"
                        size={24}
                        color="black"
                    />
                ) : null}
            </View>
        );
    }
}

I also have parrent component with array of addreses and 3 methods addAddress, changeAddress and removeAddress.

class CompanyAddresses extends React.Component<Props, State> {
    constructor(props:Props) {
        super(props);

        this.state = {
            addresses: [{address: ''}]
        }
    }

    addAddress = () => {
        this.setState({addresses: [...this.state.addresses, {address: ''}]});
    }

    changeAddress = (elemIndex:number, address:string, metro:number) => {
        let addresses = this.state.addresses;

        addresses[elemIndex] = {address: address, metro: metro};
        this.setState({addresses:addresses});
    }

    removeAddress = (elemIndex:number) => {
        const filtered = this.state.addresses.filter((value, index) => {
           return index != elemIndex;
        });

        console.log(filtered);

        this.setState({addresses: filtered})
    }

    render() {
        return (
            <View style={styles.container}>
                {this.state.addresses.map((address:any, index) =>
                    <CompanyAddress
                        changeAddress={this.changeAddress}
                        removeAddress={this.removeAddress}
                        navigation={this.props.navigation}
                        key={index}
                        index={index}
                        num={index + 1}
                    />
                )}
                {this.state.addresses.length < 5 ? (
                    <Button title="add address" onPress={this.addAddress} />
                ) : null}
            </View>
        );
    }
}

addAdress and changeAddress work well but removeAddress does not. This method doest not render right addresses after render.

For example, I added 3 addresses with name "one", "two" and "three". Then I removed address with name "two" but I see two adresses with name "one" and "two".

The most ineteresting thing is the fact that I see correct array with addresses "one" and "three" in console just before setState method.

1 Answer 1

1

The state is almost certainly being updated correctly. The issue you're seeing is due to the fact that your key inside the map is the index of the array. You can see in the docs that using the index as the key is not recommended, and this is a major reason.

We don’t recommend using indexes for keys if the order of items may change. This can negatively impact performance and may cause issues with component state.

You've modified the array so that what was at index 2 is now at index 1. This causes react to think that only the third item should be removed, and nothing else needs to change.

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

4 Comments

thanks. But how can I change my code for achieve my goal?
Make your key something unique to each entry. Only you can know what that is based on your requirements, but it could be the address value, or a combination of address and metro, or some other value.
The difficulty is the fact that when I click the button "add address" I get absolutely the same child component and I can't make unique key. Is random string is a good idea?
A random string might be more predictable, but you would take a performance hit and possibly introduce bugs at the child level. If the key changes, react will do a re-mount instead of a re-render. So if your child component has state, it will be reset each render. You'd be better off forcing some kind of uniqueness to each object.

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.