1

Hi I am pretty new to react and I want to achieve something that turns out to be real pain for me and I am not sure what the best solution might be.

I got an application that handles a single keystore as application keystore and the admin is allowed to upload new keystores and merge the entries of these keystores into the application keystore. This results in the following class definition:

export default class ApplicationKeystore extends React.Component
{

    constructor(props)
    {
        super(props);
        this.scimResourcePath = "/scim/v2/Keystore";
        this.state = {};
        this.setAliasSelectionResponse = this.setAliasSelectionResponse.bind(this);
        this.onAliasSelectionSuccess = this.onAliasSelectionSuccess.bind(this);
    }

    setAliasSelectionResponse(resource)
    {
        let copiedResource = JSON.parse(JSON.stringify(resource));
        this.setState({aliasSelectionResponse: copiedResource})
    }

    onAliasSelectionSuccess(resource)
    {
        this.setState({newAlias: resource[ScimConstants.CERT_URI].alias})
    }

    render()
    {
        return (
            <React.Fragment>
                <KeystoreUpload scimResourcePath={this.scimResourcePath}
                                setAliasSelectionResponse={this.setAliasSelectionResponse} />
                <AliasSelection scimResourcePath={this.scimResourcePath}
                                aliasSelectionResponse={this.state.aliasSelectionResponse}
                                onCreateSuccess={this.onAliasSelectionSuccess} />
                <KeystoreEntryList scimResourcePath={this.scimResourcePath}
                                   newAlias={this.state.newAlias} />
            </React.Fragment>
        )
    }
}

My problem now occurs on the last component KeystoreEntryList. The upload succeeds and the selection of entries to be merged works also correctly (just one at a time not several). But if I successfully merge an entry I get a response with the alias of the keystore entry that should now be added to the state of KeystoreEntryList. How do I do this in a clean way. I found several workarounds but all of them are dirty and make the code hard to read...

the important part of the KeystoreEntryListcan be found here:

class KeystoreEntryList extends React.Component
{
    constructor(props)
    {
        super(props);
        this.state = {aliases: []};
        this.setState = this.setState.bind(this);
        this.scimClient = new ScimClient(this.props.scimResourcePath, this.setState);
        this.onDeleteSuccess = this.onDeleteSuccess.bind(this);
    }

    async componentDidMount()
    {
        let response = await this.scimClient.listResources();
        if (response.success)
        {
            response.resource.then(listResponse =>
            {
                this.setState({
                    aliases: new Optional(listResponse.Resources[0]).map(val => val.aliases)
                                                                    .orElse([])
                })
            })
        }
    }

    componentDidUpdate(prevProps, prevState, snapshot)
    {
        // TODO if we delete an alias and add the same again the following if condition prevents adding it again
        if (prevProps.newAlias !== this.props.newAlias && this.props.newAlias !== undefined)
        {
            let aliases = this.state.aliases;
            aliases.push(this.props.newAlias);
            aliases.sort();
            this.setState({aliases: aliases, aliasDeleted: undefined});
        }
    }
   ...
}

my first idea was to do it on the componentDidUpdate method but this results in the problem stated out by the TODO. Is there any good way how I might be able to smoothly add the new alias into this component?

Goldfish
  • 614
  • 1
  • 7
  • 19

1 Answers1

1

You should not mutate the state directly as below

let aliases = this.state.aliases;
aliases.push(this.props.newAlias);

In React when the state is an [] or {} . React just holds the reference of it and check whether the reference change . Since you are pushing a value to the aliases the reference of aliases will not changes which makes react to think nothing has changed and it will not trigger a re-render.

Instead of pushing , you need to create a new copy of the array

const newAliases = [...this.state.alias, this.props.alias];
newAliases.sort;            
this.setState({aliases: newAliases, ...})
Shyam
  • 5,292
  • 1
  • 10
  • 20
  • This was actually not my question. And the update works in th eui. My problem is simply that my code is not entering the if-statement if the same alias name comes in a second time. And this brings me to the question how this can be done properly without any dirty workarounds – Goldfish May 25 '21 at 09:54
  • The way you update is still wrong in my IMHO . https://stackoverflow.com/questions/26253351/correct-modification-of-state-arrays-in-react-js – Shyam May 25 '21 at 09:56
  • good to know. I am really thankful for any advice and I will change this! – Goldfish May 25 '21 at 10:02
  • this does work! I am not quite happy with the style though but I am also not accustomed to javascript so I am not able to say if this is clean code here. But at least it keeps my code readable without really disgusting hacks. Thx for that. – Goldfish May 25 '21 at 10:18
  • how are you creating the alias. Do you have any text input where you type it manually ? – Shyam May 25 '21 at 10:21
  • it comes in a response from the server – Goldfish May 25 '21 at 10:24
  • Then the fix which i suggested should solve the purpose . But if you are fetching the alias based on some input then your componentDidUpdate will get called for each stroke . But its good that we don't have that problem . – Shyam May 25 '21 at 10:26