¿Cómo combinar múltiples objetos de estilo en línea?

214

En React puede crear claramente un objeto y asignarlo como un estilo en línea. es decir. mencionado abajo.

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);

¿Cómo puedo combinar varios objetos y asignarlos juntos?

PythonIsGreat
fuente
¿Cómo son los dos objetos?
Ryan

Respuestas:

399

Si está usando React Native, puede usar la notación de matriz:

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

Mira mi publicación de blog detallada sobre esto.

Mella
fuente
41
Lamentablemente no puedes hacer esto en React regular.
YoTengoUnLCD
8
Esta debería ser la respuesta aceptada si está usando React-Native
calcazar
Puede modificar ligeramente el estilo en línea existente agregando los nombres / valores de estilo. Por ejemplo,<View style={this.state.error == false ? styles.base : [styles.base, {backgroundColor: 'red'}]} />
Gürol Canbek
Para dos estilos estáticos, hacer StyleSheet.compose()fuera de la función de renderizado podría ser más eficiente. Producirá una nueva hoja de estilo estática que solo se enviará por el puente una vez. (Este consejo no se aplica a los estilos que el cambio en tiempo de ejecución, o que están en línea en lugar de en una hoja de estilo.)
joeytwiddle
282

Puede usar el operador de propagación:

 <button style={{...styles.panel.button,...styles.panel.backButton}}>Back</button
Nath
fuente
8
Obtengo "Error de sintaxis: token inesperado", apuntando al primer punto en el primer operador de propagación.
Izkata
@Izkata ¿Estás haciendo esto en reaccionar o reaccionar nativo, podrías publicar tu fragmento
Nath
Reaccionar, fue algo como<div style={{ ...LEGEND_STYLE.box_base, ...{ 'background': '#123456' } }} />
Izkata
Lo cambié de todos modos para usar una función auxiliar como de <div style={LEGEND_STYLE.box_gen('#123456')}todos modos, así que no es un gran problema para mí
Izkata
Esto incluso funciona si el objeto tiene solo un par clave-valor, ¡aunque se supone que los operadores de propagación solo son válidos para iterables! ¿Puedes explicar porque? developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Tyler
47

Puedes hacer esto con Object.assign().

En su ejemplo, haría:

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

Eso fusionará los dos estilos. El segundo estilo reemplazará al primero si hay propiedades coincidentes.

Como señaló Brandon, debe usarlo Object.assign({}, divStyle, divStyle2)si desea reutilizarlo divStylesin aplicar fontSize.

Me gusta usar esto para hacer componentes con propiedades predeterminadas. Por ejemplo, aquí hay un pequeño componente sin estado con un valor predeterminado margin-right:

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

Entonces podemos renderizar algo como esto:

<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>

Lo que nos dará el resultado:

<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>
qel
fuente
55
Esta solución es potencialmente mal utilizada Object.assign()de una manera que podría tener algunas consecuencias indeseables. Vea la respuesta aquí, una solución más confiable.
Brandon
1
Su respuesta demuestra una forma de mal uso de Object.assign (). Esa podría ser información útil para algunas personas, pero no se aplica a mi ejemplo. Este ejemplo no almacena el valor predeterminado en un objeto, lo muta y luego lo reutiliza en otro lugar.
qel
discrepar. abordas dos formas distintas de usar Object.assign()en tu respuesta. la primera, en las primeras oraciones, es un ejemplo del mal uso que identifiqué (es decir, si OP hizo lo que usted aconseja en su primer codebloque, él / ella mutaría {divStyle}). la segunda forma, es decir, envolverlo en una función como la que tiene, funcionará, pero la respuesta no deja en claro que está trabajando en torno a Object.assign() la inmutabilidad común 'gotcha' wrt '(aunque personalmente creo que es un poco engorroso escribir una función sin estado personalizada para cada componente predeterminado).
Brandon
Oh ok Es verdad. Si quisiera reutilizar divStyle sin aplicar fontSize, primero necesitaría el objeto vacío. Actualizaré la respuesta.
qel
Object.assign () es, con mucho, la forma más conveniente de lograr una concatenación :)
aldobsom
29

A diferencia de React Native, no podemos pasar una variedad de estilos en React, como

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

En React, necesitamos crear el único objeto de estilos antes de pasarlo a la propiedad de estilo. Me gusta:

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

  let enhancedStyle = {
    fontSize: '38px'
  }

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

Hemos utilizado el operador ES6 Spread para combinar dos estilos. También puede usar Object.assign () para el mismo propósito.

Esto también funciona si no necesita almacenar su estilo en una variable

<Segment style={{...segmentStyle, ...{height:'100%'}}}>
    Your content
</Segment>
achyut pokhrel
fuente
¿No podemos pasar la matriz al estilo?
Heisenberg
3
Solo para agregar a su último fragmento, {{segmentoStyle, height: '100%'}} también funcionará.
Luke Brandon Farrell
26

Object.assign()es una solución fácil, pero el uso (actualmente) de la respuesta principal de la misma, aunque es ideal para hacer componentes sin estado, causará problemas para el objetivo deseado del OP de fusionar dos stateobjetos.

Con dos argumentos, en Object.assign()realidad mutará el primer objeto en el lugar, afectando futuras instancias.

Ex:

Considere dos posibles configuraciones de estilo para un cuadro:

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

Por lo tanto, queremos que todas nuestras cajas tengan estilos de 'caja' predeterminados, pero queremos sobrescribir algunas con un color diferente:

// 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>

Una vez que se Object.assign()ejecuta, el objeto 'styles.box' cambia para siempre.

La solución es pasar un objeto vacío a Object.assign(). Al hacerlo, le está diciendo al método que produzca un NUEVO objeto con los objetos que le pasa. Al igual que:

// 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>

Esta noción de objetos que mutan en el lugar es crítica para React, y el uso adecuado de esto Object.assign()es realmente útil para usar bibliotecas como Redux.

Brandon
fuente
1
Su ejemplo de cómo usar mal Object.assign no es una representación válida de cómo se está utilizando Object.assign en la respuesta principal (actualmente).
qel
gracias, hizo ediciones para reflejar qué aspecto es (actualmente) un mal uso
Brandon
1
bonito código justo ahí. Los condicionales Object.assign ({}, this.state.showStartOverBtn? {}: This.hidden, this.btnStyle)} también funcionan como un encanto
steveinatorx
17

Array notaion es la mejor manera de combinar estilos en react native.

Esto muestra cómo combinar 2 objetos de estilo,

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

esto muestra cómo combinar el objeto y la propiedad Style,

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

Esto funcionará en cualquier aplicación nativa de reacción.

Sasindu Lakshitha
fuente
2
Las preguntas son para React, noReact Native
Dimitar Tsonev
Puedes usar lo mismo para React también
Sasindu Lakshitha
React no admite la matriz desde el principio y ahora ya no funciona.
witoong623
Reaccionar aún no admite combinar estilo como este.
enojado kiwi
12

También puede combinar clases con un estilo en línea como este:

<View style={[className, {paddingTop: 25}]}>
  <Text>Some Text</Text>
</View>
Gil Pérez
fuente
8

En realidad, hay una forma formal de combinar y es como a continuación:

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

Pero, hay un pequeño problema , si uno de ellos es pasado por el componente padre y fue creado por una forma formal combinada, tenemos un gran problema:

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

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

Y esta última línea hace que tenga un error de interfaz de usuario, hosco, React Native no puede lidiar con una matriz profunda dentro de una matriz. Entonces creo mi función auxiliar:

import { StyleSheet } from 'react-native';

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

Al usar my en styleJoinercualquier lugar, puede combinar cualquier tipo de estilo y combinar estilos. incluso undefinedu otros tipos inútiles no hacen que se rompa el estilo.

AmerllicA
fuente
5

Descubrí que esto funciona mejor para mí. Se anula como se esperaba.

return <View style={{...styles.local, ...styles.fromProps}} />
Adrian Bartholomew
fuente
2

Para aquellos que buscan esta solución en React, si desea usar el operador spread dentro del estilo, debe usar: babel-plugin-transform-object-rest-spread.

Instálelo mediante el módulo npm y configure su .babelrc como tal:

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

Entonces puedes usar como ...

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

Más información: https://babeljs.io/docs/en/babel-plugin-transform-object-rest-spread/

Fénix
fuente
2

Para llevar esto aún más lejos, puede crear una función auxiliar similar a los nombres de clase :

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

Y luego úselo condicionalmente en sus componentes:

<div style={

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

}>Hello World!</div>
streletss
fuente
0

Así que básicamente estoy viendo esto de manera incorrecta. Por lo que veo, esta no es una pregunta específica de React, más una pregunta de JavaScript sobre cómo combino dos objetos de JavaScript juntos (sin alterar propiedades con nombres similares).

En esta respuesta de StackOverflow, lo explica. ¿Cómo puedo fusionar propiedades de dos objetos JavaScript dinámicamente?

En jQuery puedo usar el método extend.

PythonIsGreat
fuente
1
Solo fusionar objetos no es suficiente cuando se trabaja con objetos que contienen estilos CSS. Hay propiedades de taquigrafía, mayúsculas y minúsculas diferentes, etc. que deben manejarse en el orden correcto mediante la función de fusión. Estoy buscando y aún no he encontrado una biblioteca que proporcione dicha función.
sompylasar
Aquí hay una biblioteca que hace la expansión de propiedades abreviadas: github.com/kapetan/css-shorthand-expand , pero esta no es una solución completa.
sompylasar
0

Para ampliar lo que dijo @PythonIsGreat, creo una función global que lo hará por mí:

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

Esto extiende profundamente los objetos a un nuevo objeto y permite un número variable de objetos como parámetros. Esto le permite hacer algo como esto:

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

var styles = {
  base:{
    //whatever
  },
  first:{
    //whatever
  },
  second:{
    //whatever
  }
}
timthez
fuente
Una función de fusión de objetos simples no es suficiente cuando se trabaja con objetos que contienen estilos CSS. Vea mi comentario a la respuesta anterior.
sompylasar
0

Formas de estilo en línea:

<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>
él
fuente
3
React no admite la matriz desde el principio y ahora ya no funciona.
witoong623