Назначьте стиль и реквизит каждому вложенному дочернему элементу

Я хочу выборочно передать некоторые реквизиты каждому дочернему элементу и стилизовать каждый из них.

const Burger = ({children, ...rest}) => (
  <>
   <p>I'm a burger...</p>
   {children}
  </>
)
const Meat = ({type}) => <span>with {type?type:'no'} meat</span>
const Sauce = ({flavor}) => <span>with {flavor?flavor:'no'} sauce</span>
const Veggie = ({green}) => <span>with {green?green:'no'} greens</span>

const App = () => (
  // reach-router nesting
  <Router>
    <Burger path='burger' {...props}>
      <Sauce path='sauce' />
      <Veggie path='veggie' />
      <Meat path='meat' />
    </Burger>
  </Router>
)

Я заметил, что не могу просто перебирать и передавать реквизиты каждому ребенку.

const Burger = ({children, ...rest}) => (
   <p>I'm a burger... </p>
   {children.map(Child => <Child {...rest} />) } // error
)

Использование функции рендеринга не работает внутри <router>. Это нарушает вложенную маршрутизацию, и мне не нравится вручную вводить реквизиты для каждого дочернего элемента, если количество дочерних элементов неопределенно.

...
  <Router>
    <Burger path='burger' {...props}>
    {someStyle => (
      <>
        <Sauce path='sauce' style={someStyle}/>
        <Veggie path='veggie' style={someStyle}/>
        <Meat path='meat' style={someStyle}/>
      </>
    )}
    </Burger>
  </Router>
...

затем я пошел дальше и попытался использовать решение css-in-js (эмоции) для выбора и стилизации каждого прямого потомка <Burger/>, но в этом случае это также не работает.

const styleAllChild = css`
  & > * : {
    margin-bottom: 10px;
  }
`
...
  <Router>
    <Burger path='burger' css={styleAllChild} {...props}>
      <Sauce path='sauce' />
      <Veggie path='veggie' />
      <Meat path='meat' />
    </Burger>
  </Router>
...

Наконец, я нахожу решение использования children.map в сочетании с React.cloneElement неудовлетворительным, поскольку должны быть некоторые последствия для производительности, если каждый дочерний элемент слишком большой или слишком вложенный.

Есть ли другой способ реализовать то, что я хочу? Спасибо.


person Terry    schedule 18.12.2019    source источник


Ответы (2)


Ваше первое решение должно работать. Вы должны использовать только один компонент в операторе return. Поэтому я добавил ‹> (сокращение от Fragments). И вы должны использовать двойные фигурные скобки для деструктуризации (первая для использования кода js в синтаксисе jsx, вторая для синтаксиса деструктуризации объекта).

const Burger = ({children, ...rest}) => (
  <>
   <p>I'm a burger... </p>
   {children.map(Child => <Child {{...rest}} />) } // error
  </>
)
person Peter Ambruzs    schedule 18.12.2019

Я решил это, используя ответ Питера, но вместо этого с React.Children

const Parent = ({children, myProps}) => (
  <>
  ...
  {React.Children.map(children || null, (child, index) => {
    return (
      <child 
        {...child.props}
        key={index}
        myProps={myProps}
        style={someStyle} 
      />
    );
  })}
  ...
  <>
person Terry    schedule 18.12.2019