Search code examples
reactjslodashjsxmemoise

Memoize and lodash times get index, create unique text inputs


I have an icon button with a plus sign. Each time when I click it I'm creating a text input. I simplified the code in the example but in my project I am trying to use it to create social icons, where in each text input you add the social icon name/or url. The code is JSX in React:

export default class Test extends Component {
    render() {  

        const { attributes: { totalItems, textInputValue }, setAttributes } = this.props;

        const createItem = memoize( ( totalItems ) => {
            return times( totalItems, () => renderItems( totalItems, textInputValue ) );
        } );

         return (
            <IconButton                 
              label={ __( 'Add text input' ) }
              icon="plus"
              onClick={ () => setAttributes( { totalItems: totalItems + 1 } ) }
            />

            { createItem( totalItems ) }
        )
    }
}

function renderItems( index, textInputValue  ) {
    return (

     <TextControl
        label={ __( 'My text input' ) }
        value={ textInputValue }
        onChange={ ( value ) => setAttributes( { textInputValue: value } ) }
     />  /* how can I get unique text inputs? */

     { index } /* this doesn't return the index of the element created */
    )
}

The problem: the same text input is being created. Is there a way to add index or map to memoize/times in order to render unique inputs?


Solution

  • Lodash's _.times() returns the index to the callback:

    const totalItems = 5;
    
    const result = _.times(totalItems, index => ({ index }));
    
    console.log(result);
    <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>

    So in your case, the component should be something like this:

    Note: I've removed memoize, since calling it multiple times in render doesn't actually memoize anything. If you need memoize, you should move the function creation to a property of the object, so calls would be cached.

    export default class Test extends Component {
        render() {  
            const { attributes: { totalItems, textInputValue }, setAttributes } = this.props;
    
            const createItem = (totalItems, textInputValue) =>
              times(totalItems, index => renderItems(index, textInputValue );
    
             return (
                <IconButton                 
                  label={ __( 'Add text input' ) }
                  icon="plus"
                  onClick={ () => setAttributes( { totalItems: totalItems + 1 } ) }
                />
    
                { createItem( totalItems ) }
            )
        }
    }