How to reuse get_template_part() containing a simple loop to work with AJAX correctly?

From what I’ve used and seen, you don’t put the whole loop into the Template Part, just the layout. The fact is you’re always going to have a loop of some kind, the difference is the layout of the contents of that loop. You should break it out like this:

if( have_posts() ) {

    while( have_posts() ) {

        the_post();
        get_template_part( 'inc/loop/layout', 'simple' );

    }

}

Now if you really want to pull the exact same query in your AJAX call you could say:

rewind_posts();

ob_start();

// Same loop as above

$output = ob_get_clean();

The rewind_posts() will start the global post loop back at index 0 so you can run the loop over again. In reality, you’re more likely to create a new WP_Query and loop though whatever those posts are ( offset? pagination? ) and run through the same loop method with the new WP_Query object but still using the same simple layout template part:

$query = new WP_Query( $args );

ob_start();

if( $query->have_posts() ) {

    while( $query->have_posts() ) {

        $query->the_post();
        get_template_part( 'inc/loop/layout', 'simple' );

    }

}

$output = ob_get_clean();

In the end we’re just using output buffering to loop though any objects but the template part provides those objects with the same kind of layout.