Как объединить несколько встроенных объектов стиля?

214

В React вы можете четко создать объект и назначить его как встроенный стиль. то есть. упомянуто ниже.

var divStyle = {
  color: 'white',
  backgroundImage: 'url(' + imgUrl + ')',
  WebkitTransition: 'all', // note the capital 'W' here
  msTransition: 'all' // 'ms' is the only lowercase vendor prefix
};

var divStyle2 = {fontSize: '18px'};

React.render(<div style={divStyle}>Hello World!</div>, mountNode);

Как я могу объединить несколько объектов и назначить их вместе?

PythonIsGreat
источник
Как выглядят эти два объекта?
Райан

Ответы:

400

Если вы используете React Native, вы можете использовать обозначение массива:

<View style={[styles.base, styles.background]} />

Проверьте мой подробный пост в блоге об этом.

Ник
источник
42
К сожалению, вы не можете сделать это в обычном React.
YoTengoUnLCD
8
Это должен быть принятый ответ, если вы используете React-Native
calcazar
Вы можете немного изменить существующий встроенный стиль, добавив имена / значения стиля. Например,<View style={this.state.error == false ? styles.base : [styles.base, {backgroundColor: 'red'}]} />
Гюрол Канбек,
Для двух статических стилей выполнение StyleSheet.compose()вне функции рендеринга может быть более эффективным. Он создаст новую статическую таблицу стилей, которая будет передана по мосту только один раз. (Этот совет не относится к стилям, которые изменяются во время выполнения или являются встроенными вместо таблицы стилей.)
joeytwiddle
283

Вы можете использовать оператор распространения:

 <button style={{...styles.panel.button,...styles.panel.backButton}}>Back</button
Нат
источник
8
Я получаю «Синтаксическая ошибка: неожиданный токен», указывающий на первую точку в первом операторе спреда.
Изката
@Izkata Вы делаете это в реакции или реагировать нативно, не могли бы вы опубликовать свой фрагмент
Нат
Реакция, это было что-то вроде<div style={{ ...LEGEND_STYLE.box_base, ...{ 'background': '#123456' } }} />
Izkata
В любом случае я изменил его, чтобы использовать вспомогательную функцию <div style={LEGEND_STYLE.box_gen('#123456')}, так что для меня это не имеет большого значения
Izkata
Это даже работает, если объект имеет только одну пару ключ-значение, даже если операторы распространения должны быть действительными только для итераций! Вы можете объяснить, почему? developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Тайлер
47

Вы можете сделать это с Object.assign().

В вашем примере вы бы сделали:

ReactDOM.render(
    <div style={Object.assign(divStyle, divStyle2)}>
        Hello World!
    </div>,
    mountNode
);

Это объединит два стиля. Второй стиль заменит первый, если есть соответствующие свойства.

Как заметил Брэндон, вы должны использовать его, Object.assign({}, divStyle, divStyle2)если хотите использовать повторно divStyleбез применения fontSize.

Мне нравится использовать это для создания компонентов со свойствами по умолчанию. Например, вот небольшой компонент без сохранения состояния по умолчанию margin-right:

const DivWithDefaults = ({ style, children, ...otherProps }) =>
    <div style={Object.assign({ marginRight: "1.5em" }, style)} {...otherProps}>
        {children}
    </div>;

Таким образом, мы можем сделать что-то вроде этого:

<DivWithDefaults>
    Some text.
</DivWithDefaults>
<DivWithDefaults className="someClass" style={{ width: "50%" }}>
    Some more text.
</DivWithDefaults>
<DivWithDefaults id="someID" style={{ marginRight: "10px", height: "20px"}}>
    Even more text.
</DivWithDefaults>

Что даст нам результат:

<div style="margin-right:1.5em;">Some text.</div>
<div style="margin-right:1.5em;width50%;" class="someClass">Some more text.</div>
<div style="margin-right:10px;height:20px;" id="someID">Even more text.</div>
Кель
источник
5
это решение может быть использовано не Object.assign()по назначению, что приведет к нежелательным последствиям. Смотрите ответ здесь, более надежное решение.
Брэндон
1
Ваш ответ демонстрирует способ неправильного использования Object.assign (). Это может быть полезной информацией для некоторых людей, но это не относится к моему примеру. Этот пример не хранит объект по умолчанию, не изменяет его, а затем повторно использует измененный объект где-то еще.
Qel
не согласны. Вы обращаетесь к двум отдельным способам использования Object.assign()в своем ответе. первое, в первых нескольких предложениях, является примером выявленного мною неправильного использования (т. е. если бы ОП выполнил то, что вы посоветовали в первом codeблоке, он / она мутировал {divStyle}). второй способ - то есть, завернуть его в функцию, как у вас, будет работать, но ответ не дает понять, что вы работаете над Object.assign() «обычной« ошибкой »в отношении неизменности (хотя лично я думаю, что это немного неудобно писать пользовательскую функцию без сохранения состояния для каждого компонента по умолчанию).
Брэндон
О хорошо Это правда. Если он хочет повторно использовать divStyle без применения fontSize, ему сначала понадобится пустой объект. Я обновлю ответ.
Qel
Object.assign () - безусловно, самый удобный способ для достижения конкатенации :)
aldobsom
29

В отличие от React Native, мы не можем передать массив стилей в React, например

<View style={[style1, style2]} />

В React нам нужно создать отдельный объект стилей перед передачей его в свойство style. Подобно:

const Header = (props) => {
  let baseStyle = {
    color: 'red',
  }

  let enhancedStyle = {
    fontSize: '38px'
  }

  return(
    <h1 style={{...baseStyle, ...enhancedStyle}}>{props.title}</h1>
  );
}

Мы использовали оператор ES6 Spread для объединения двух стилей. Вы также можете использовать Object.assign () для той же цели.

Это также работает, если вам не нужно хранить свой стиль в var

<Segment style={{...segmentStyle, ...{height:'100%'}}}>
    Your content
</Segment>
ахют похрел
источник
Мы не можем передать массив в стиль?
Гейзенберг
3
Просто добавьте к вашему последнему фрагменту, {{... сегментStyle, height: '100%'}} также будет работать.
Люк Брэндон Фаррелл,
26

Object.assign()это простое решение, но его использование (в настоящее время) является лучшим ответом - хотя оно прекрасно подходит для создания компонентов без состояния, но вызовет проблемы для желаемой цели OP - объединение двух stateобъектов.

С двумя аргументами Object.assign()будет фактически мутировать первый объект на месте, влияя на будущие экземпляры.

Пример:

Рассмотрим две возможные конфигурации стилей для коробки:

var styles =  {
  box: {backgroundColor: 'yellow', height: '100px', width: '200px'},
  boxA: {backgroundColor: 'blue'},
};

Таким образом, мы хотим, чтобы все наши блоки имели стили «по умолчанию», но хотим перезаписать их другим цветом:

// this will be yellow
<div style={styles.box}></div>

// this will be blue
<div style={Object.assign(styles.box, styles.boxA)}></div>

// this SHOULD be yellow, but it's blue.
<div style={styles.box}></div>

После Object.assign()выполнения объект 'styles.box' изменяется навсегда.

Решение состоит в том, чтобы передать пустой объект Object.assign(). При этом вы говорите методу для создания нового объекта с объектами, которые вы передаете ему. Вот так:

// this will be yellow
<div style={styles.box}></div>

// this will be blue
<div style={Object.assign({}, styles.box, styles.boxA)}></div>

// a beautiful yellow
<div style={styles.box}></div>

Это понятие объектов, мутирующих на месте, является критическим для React, и правильное использование Object.assign()действительно полезно для использования таких библиотек, как Redux.

Brandon
источник
1
Ваш пример неправильного использования Object.assign не является верным представлением о том, как Object.assign используется в (в настоящее время) верхнем ответе.
Qel
спасибо, внес изменения, чтобы отразить, какой аспект (в настоящее время) используется не по назначению
Брэндон
1
хороший кусок кода прямо там. Условные выражения Object.assign ({}, this.state.showStartOverBtn? {}: This.hidden, this.btnStyle)} также работают как талисман
steveinatorx
17

Array notaion - лучший способ сочетания стилей в реагировать на нативные.

Это показывает, как объединить 2 объекта стиля,

<Text style={[styles.base, styles.background]} >Test </Text>

это показывает, как объединить объект и свойство стиля,

<Text style={[styles.base, {color: 'red'}]} >Test </Text>

Это будет работать на любое реагирующее нативное приложение.

Сасинду Лакшита
источник
2
Вопросы для React, а не дляReact Native
Димитар Цонев
Вы можете использовать то же самое для Реагировать также
Сасинду Лакшита
React не поддерживает массив с самого начала, и теперь он больше не работает.
witoong623
реагировать еще не поддерживать объединить стиль, как это.
злой киви
12

Вы также можете комбинировать классы со встроенным стилем так:

<View style={[className, {paddingTop: 25}]}>
  <Text>Some Text</Text>
</View>
Хил Перес
источник
8

На самом деле, есть формальный способ объединения, и это как ниже:

<View style={[style01, style02]} />

Но есть небольшая проблема , если один из них передается родительским компонентом, и он был создан комбинированным формальным способом, у нас есть большая проблема:

// The passing style02 from props: [parentStyle01, parentStyle02]

// Now:
<View style={[style01, [parentStyle01, parentStyle02]]} />

И эта последняя строка вызывает ошибку пользовательского интерфейса, React Native не может работать с глубоким массивом внутри массива. Итак, я создаю свою вспомогательную функцию:

import { StyleSheet } from 'react-native';

const styleJoiner = (...arg) => StyleSheet.flatten(arg);

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

AmerllicA
источник
5

Я обнаружил, что это работает лучше всего для меня. Это переопределяет, как и ожидалось.

return <View style={{...styles.local, ...styles.fromProps}} />
Адриан Варфоломей
источник
2

Для тех, кто ищет это решение в React, если вы хотите использовать оператор распространения внутри стиля, вы должны использовать: babel-plugin-transform-object-rest-spread.

Установите его с помощью модуля npm и настройте ваш .babelrc следующим образом:

{
  "presets": ["env", "react"],
  "plugins": ["transform-object-rest-spread"]
}

Тогда вы можете использовать как ...

const sizing = { width: 200, height: 200 }
 <div
   className="dragon-avatar-image-background"
   style={{ backgroundColor: blue, ...sizing }}
  />

Дополнительная информация: https://babeljs.io/docs/en/babel-plugin-transform-object-rest-spread/

Феникс
источник
2

Чтобы продвинуться в этом еще дальше, вы можете создать вспомогательную функцию, аналогичную именам классов:

const styleRules = (...rules) => {
  return rules.filter(Boolean).reduce((result, rule) => {
    return { ...result, ...rule };
  }, {});
};

А затем используйте его условно в ваших компонентах:

<div style={

  styleRules(
    divStyle,
    (window.innerWidth >= 768) && divStyleMd,
    (window.innerWidth < 768) && divStyleSm
  )

}>Hello World!</div>
streletss
источник
0

В общем, я смотрю на это неправильно. Из того, что я вижу, это не конкретный вопрос React, а скорее вопрос JavaScript о том, как объединить два объекта JavaScript вместе (без заусенцев с одноименными свойствами).

В этом ответе StackOverflow это объясняет это. Как динамически объединить свойства двух объектов JavaScript?

В jQuery я могу использовать метод расширения.

PythonIsGreat
источник
1
Простого объединения объектов недостаточно при работе с объектами, которые содержат стили CSS. Существуют сокращенные свойства, различные регистры букв и т. Д., Которые должны обрабатываться в правильном порядке с помощью функции слияния. Я ищу и еще не нашел библиотеку, которая обеспечивает такую ​​функцию.
Сомпиласар
Вот библиотека, которая сокращает свойства: github.com/kapetan/css-shorthand-expand , но это не полное решение.
Сомпиласар
0

Чтобы расширить сказанное @PythonIsGreat, я создаю глобальную функцию, которая сделает это за меня:

var css = function(){
    var args = $.merge([true, {}], Array.prototype.splice.call(arguments, 0));
    return $.extend.apply(null, args);
}

Это глубоко расширяет объекты в новый объект и учитывает переменное количество объектов в качестве параметров. Это позволяет вам сделать что-то вроде этого:

return(
<div style={css(styles.base, styles.first, styles.second,...)} ></div>
);

var styles = {
  base:{
    //whatever
  },
  first:{
    //whatever
  },
  second:{
    //whatever
  }
}
timthez
источник
Одной функции слияния простых объектов недостаточно при работе с объектами, которые содержат стили CSS. Смотрите мой комментарий к ответу выше.
Сомпиласар
0

Способы встроенного моделирования:

<View style={[styles.red, {fontSize: 25}]}>
  <Text>Hello World</Text>
</View>

<View style={[styles.red, styles.blue]}>
  <Text>Hello World</Text>
</View>

  <View style={{fontSize:10,marginTop:10}}>
  <Text>Hello World</Text>
</View>
ему
источник
3
React не поддерживает массив с самого начала, и теперь он больше не работает.
witoong623