Понял как нужно делать. Уже созданный JSX изменить нельзя, но можно его обойти и создавать копии элементов. Это делает функция React.cloneElement(). Она принимает элемент для клонирования, объект с его атрибутами и детей. Для создания элемента нужно использовать React.createElement(). Там почти такой же набор параметров: имя тега, его атрибуты и массив детей. Эти две функции решат задачу.
function App() {
let markup = (
<section>
<p /><p />
<footer />
</section>
);
// Вставляемый компонент
function Span() { return <span>Span elem</span> }
// Рекурсивная функция принимает JSX и изменяет разметку.
function enhanceMarkup(elem) {
// Если тегом элемента является section, то обернуть его детей в <aside>.
if(elem.type === 'section') {
// Составлю массив детей элемента <section>
let childrenArr = elem.props.children.map((child, i) => {
return React.cloneElement(
// Функция запускается рекурсивно чтобы при следующем запуске обработать <p>
enhanceMarkup(child), {key: i}
);
});
// Сделаю новый элемент <aside> куда помещу детей <section>
let aside = React.createElement('aside', null, childrenArr);
// Клонирую <section> и поставлю в качестве потомка <aside>
return React.cloneElement(elem, elem.props, aside)
}
// Если тегом элемента является p, то поставить компонент <Span /> потомком
if(elem.type === 'p') {
// Клонирую элемент и поставлю компонент как ребёнка
return React.cloneElement(elem, elem.props, <Span />)
}
// Возвратить неизменный elem если его тегом не является section или p (которые обрабатываются и возвращаются в коде выше).
return elem;
}
// Изменить и вернуть новую разметку markup
return enhanceMarkup(markup)
}