Объединять классы с JSS вместо переопределения - PullRequest
0 голосов
/ 07 декабря 2018

Я использую React и JSS, чтобы попытаться создать небольшой набор повторно используемых компонентов, но я сталкиваюсь с проблемами, пытаясь объединить базовые компоненты в более сложные.

Когда я передаю имена классов внизиз родительского компонента с помощью classes, он полностью переопределяет стили по умолчанию, а не объединяет их.См. Фрагмент ниже для демонстрации: при переносе текстовый ввод теряет стили границ и принимает только ширину, указанную в оболочке.

const { Component } = React;
const { render } = ReactDOM;
const injectSheet = reactJss.default;

// TextInput.jsx
const TextInput = (() => {
  const styles = {
    root: {
      border: '1px solid #ccc',
      borderRadius: 3,
    }
  };
  const TextInput = ({ classes, ...rest }) => (
    <input className={classes.root} type="text" {...rest} />
  );
  return injectSheet(styles)(TextInput);
})();

// InputField.jsx
const InputField = (() => {
  const styles = {
    root: {
      display: 'inline-flex',
    },
    label: {
      marginRight: 5,
      width: 40,
    },
    input: {
      width: 80,
    },
  };
  const InputField = ({ classes, id, label }) => (
    <span className={classes.root}>
      <label className={classes.label} htmlFor={id}>{label}</label>
      <TextInput classes={{root: classes.input}} id={id} />
    </span>
  );
  return injectSheet(styles)(InputField);
})();


// Demonstration
class App extends Component {
  render() {
    return (
      <dl>
        <dt>Without Wrapper:</dt>
        <dd><TextInput /></dd>
        <dt>With Wrapper:</dt>
        <dd><InputField id="f" label="Foo"/></dd>
        <dt>What I want:</dt>
        <dd><InputField classes={{input: 'generated-by-TextInput-root generated-by-InputField-input'}} id="b" label="Foo"/></dd>
      </dl>
    );
  }
}

render(<App />, document.getElementById('root'));
.generated-by-TextInput-root {
  border: 1px solid #ccc;
  border-radius: 3px;
}

.generated-by-InputField-input {
  width: 80px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jss/9.8.7/jss.min.js"></script>
<script src="https://unpkg.com/react-jss@8.6.1/dist/react-jss.min.js"></script>
<div id="root"></div>

Вместо простого переопределения имени класса, сгенерированного внутренним компонентом, я хочу применить этот класс, а затем пользовательские стили внешних компонентов поверхЭто.По сути, я бы хотел, чтобы classes опора работала в добавке .

Есть ли способ сделать это изящно?

Ответы [ 5 ]

0 голосов
/ 10 декабря 2018

Я создал проблему, чтобы исследовать, можем ли мы / должны ли добавить поведение слияния к реагировать-jss https://github.com/cssinjs/jss/issues/933

0 голосов
/ 07 декабря 2018

Вот альтернативный способ решения проблемы.Вместо того, чтобы пытаться передать класс от родителя для объединения с дочерними классами, воздействуйте на ребенка, используя стили родителя (например, '& > input': { width: 80 }).

const { Component } = React;
const { render } = ReactDOM;
const injectSheet = reactJss.default;

// TextInput.jsx
const TextInput = (() => {
  const styles = {
    root: {
      border: '1px solid #ccc',
      borderRadius: 3,
    }
  };
  const TextInput = ({ classes, ...rest }) => (
    <input className={classes.root} type="text" {...rest} />
  );
  return injectSheet(styles)(TextInput);
})();

// InputField.jsx
const InputField = (() => {
  const styles = {
    root: {
      display: 'inline-flex',
      '& > input': {
        width: 80
      }
    },
    label: {
      marginRight: 5,
      width: 40,
    }
  };
  const InputField = ({ classes, id, label }) => (
    <span className={classes.root}>
      <label className={classes.label} htmlFor={id}>{label}</label>
      <TextInput id={id} />
    </span>
  );
  return injectSheet(styles)(InputField);
})();


// Demonstration
class App extends Component {
  render() {
    return (
      <dl>
        <dt>Without Wrapper:</dt>
        <dd><TextInput /></dd>
        <dt>With Wrapper:</dt>
        <dd><InputField id="f" label="Foo"/></dd>
      </dl>
    );
  }
}

render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jss/9.8.7/jss.min.js"></script>
<script src="https://unpkg.com/react-jss@8.6.1/dist/react-jss.min.js"></script>
<div id="root"></div>
0 голосов
/ 07 декабря 2018

Я думаю, вы хотите что-то вроде следующего:

...
const TextInput = ({ classes, classesFromParent, ...rest }) => (
    <input className={classes.root + ' ' + classesFromParent.root} type="text" {...rest} />
);
...
const InputField = ({ classes, id, label }) => (
    <span className={classes.root}>
      <label className={classes.label} htmlFor={id}>{label}</label>
      <TextInput classesFromParent={{root: classes.input}} id={id} />
    </span>
);
0 голосов
/ 07 декабря 2018

Аналогично одному из других решений, но более устойчиво для работы с дополнительными классами.Мы можем использовать Object.values(classes).join(' ') для объединения всех классов, переданных в компонент TextInput.

Обратите внимание, что я также должен был дать классу другой ключ.classes={{root: classes.input}} стало classes={{input: classes.input}}.

const { Component } = React;
const { render } = ReactDOM;
const injectSheet = reactJss.default;

// TextInput.jsx
const TextInput = (() => {
  const styles = {
    root: {
      border: '1px solid #ccc',
      borderRadius: 3,
    }
  };
  const TextInput = ({ classes, ...rest }) => (
      <input className={Object.values(classes).join(' ')} type="text" {...rest} />
  );
  return injectSheet(styles)(TextInput);
})();

// InputField.jsx
const InputField = (() => {
  const styles = {
    root: {
      display: 'inline-flex',
    },
    label: {
      marginRight: 5,
      width: 40,
    },
    input: {
      width: 80,
    },
  };
  const InputField = ({ classes, id, label }) => (<span className={classes.root}>
      <label className={classes.label} htmlFor={id}>{label}</label>
      <TextInput classes={{input: classes.input}} id={id} />
    </span>
  );
  return injectSheet(styles)(InputField);
})();


// Demonstration
class App extends Component {
  render() {
    return (
      <dl>
        <dt>Without Wrapper:</dt>
        <dd><TextInput /></dd>
        <dt>With Wrapper:</dt>
        <dd><InputField id="f" label="Foo"/></dd>
        <dt>What I want:</dt>
        <dd><InputField classes={{input: 'generated-by-TextInput-root generated-by-InputField-input'}} id="b" label="Foo"/></dd>
      </dl>
    );
  }
}

render(<App />, document.getElementById('root'));
.generated-by-TextInput-root {
  border: 1px solid #ccc;
  border-radius: 3px;
}

.generated-by-InputField-input {
  width: 80px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jss/9.8.7/jss.min.js"></script>
<script src="https://unpkg.com/react-jss@8.6.1/dist/react-jss.min.js"></script>
<div id="root"></div>
0 голосов
/ 07 декабря 2018

Мы пошли за первостепенное поведение.В некоторых случаях вы хотите добавить свой класс, в некоторых других вы хотите переопределить их, но я не думаю, что мы не можем иметь оба одновременно.

...