I am creating a simple React component that displays images in a list/array.
The source is shown below:
import React from "react";
import styled from "styled-components";
import burgerTop from "../../assets/images/burgerTop.png";
import burgerBottom from "../../assets/images/burgerBottom.png";
const BurgerComponent = props => {
console.log(JSON.stringify(props));
const BurgerDiv = styled.div`
width: 500px;
margin: 0 auto;
text-align: center;
display: flex;
flex-direction: column;
`;
return (
<BurgerDiv>
{props.burger.map(burgerItem => {
console.log(burgerItem);
const imgSrc = burgerItem.img;
console.log(imgSrc);
return <img key={burgerItem.id} src={imgSrc} />;
})}
</BurgerDiv>
);
};
export default BurgerComponent;
Props are shown as below
{
"burger":[
{
"type":"top",
"img":"burgerTop",
"value":1,
"id":"sss2",
"addable":false
},
{
"type":"bottom",
"img":"burgerBottom",
"value":1,
"id":"aaa7",
"addable":false
}
]
}
But the image path is not taken from import.
Not sure what I am doing wrong.
Please help.
img's source property should be a URL, or base64 string. Your burger prop doesn't have a source property like that.
I noticed that you are importing burgerTop and burgerBottom from assets folder. Maybe you can use those images like this:
{props.burger.map(burgerItem => {
const imgSrc = burgerItem.img === "burgerTop" ? burgerTop : burgerBottom ;
return <img key={burgerItem.id} src={imgSrc} />;
})}
Related
can somebody explain how pass in the defauklt theme in Material UI5
in Material UI6 i use to do it like this
const useStyles = makeStyles((theme) => ({
home: {
display: "flex",
paddingTop: "8rem",
width: "100vw",
height: "100vh",
backgroundColor: theme.palette.primary.dark,
color: "white",
},
}));
but as i got throught M-UI5 docs (as far as i found) there is no explanation on how it can be done , the only part they mention about makeStyle it contains this code in this page docs
+import { makeStyles } from '#mui/styles';
+import { createTheme, ThemeProvider } from '#mui/material/styles';
+const theme = createTheme();
const useStyles = makeStyles((theme) => ({
background: theme.palette.primary.main,
}));
function Component() {
const classes = useStyles();
return <div className={classes.root} />
}
// In the root of your app
function App(props) {
- return <Component />;
+ return <ThemeProvider theme={theme}><Component {...props} /></ThemeProvider>;
}
so am i suppose to run createTheme() on every component to get the theme? , apology if i missed out an obvious thing in the docs , probably coz my poor english
The part you are missing is from this part of the migration guide: https://mui.com/material-ui/guides/migration-v4/#style-library.
if you are using JSS style overrides for your components (for example overrides created by makeStyles), you will need to take care of the CSS injection order. To do so, you need to have the StyledEngineProvider with the injectFirst option at the top of your component tree.
Without this, the default styles for the MUI Card in your About component win over the styles specified via classes.about where the styles overlap (e.g. background).
Changing your AllProviders component to the following (just adding <StyledEngineProvider injectFirst>) fixes it:
import React from "react";
import CountriesProvider from "./countries-context";
import QuestionsProvider from "./questions-context";
import {
ThemeProvider,
createTheme,
StyledEngineProvider
} from "#mui/material/styles";
const theme = createTheme();
const AllProviders = (props) => {
return (
<StyledEngineProvider injectFirst>
<QuestionsProvider>
<ThemeProvider theme={theme}>
<CountriesProvider>{props.children}</CountriesProvider>
</ThemeProvider>
</QuestionsProvider>
</StyledEngineProvider>
);
};
export default AllProviders;
https://codesandbox.io/s/funny-flower-w9dzen?file=/src/store/AllProviders.js:303-342
The theme was being passed in fine without this change (otherwise you would have had errors when it tried to access parts of the theme), but the CSS injection order was not correct.
So on a project I'm working on I want to be able to update the theme of my website when a user changes the currentTheme variable which is set up using useState, and references some objects I have in another file with predefined styles. I am having trouble updating and setting these styles up using styled-components.
I currently have it so whenever the user changes the theme(which is done in another component) it will update the current chosen theme. As you can see there's a console.log there, and the background colour does update accordingly, it just doesn't work with the styled-components. I would like to state that I have tried moving the styled.div into my CodeContainer component and it worked there, but the performance of the app was HORRIBLE when I made that change.
I basically need to know how to update a variable in the styled.div. Should I pass through props instead? Should I move it in another way? Not too sure what to do here. Thanks in advance for any help!
import React, { useState } from "react";
import Editor from "./Editor";
import { monokai, solarizedDark, terminal, textmate } from "./ui/themes";
import styled from "styled-components";
const CodeContainer = ({ setHtml, setCss, setJs, setRenderDoc, theme }) => {
let currentTheme = monokai;
if (theme === "textmate") {
currentTheme = textmate;
} else if (theme === "solarized_dark") {
currentTheme = solarizedDark;
} else if (theme === "terminal") {
currentTheme = terminal;
}
return (
<Container background={currentTheme}>
<Editor
setHtml={setHtml}
setRenderDoc={setRenderDoc}
languageName="html"
theme={theme}
/>
<Editor
setCss={setCss}
setRenderDoc={setRenderDoc}
languageName="css"
theme={theme}
/>
<Editor
setJs={setJs}
setRenderDoc={setRenderDoc}
languageName="javascript"
theme={theme}
/>
</Container>
);
};
export default CodeContainer;
const Container = styled.div`
background: ${(currentTheme) => currentTheme.background};
height: 40vh;
border-bottom: 4px #211e1c solid;
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 15px;
`;
Use the following syntax. Docs.
const Container = styled.div((props) => ({
background: props.background,
height: "40vh",
borderBottom: "4px #211e1c solid",
//..
}));
Note: Make sure to use the camelCase syntax with CSS properties.
I am using emotion and typescript. I have a component which (roughly) looks as follows
import styled from '#emotion/styled';
export default Box = styled.div`
display: flex;
align-items: center;
justify-content: center;
`;
In another component, I am using that div but using the as prop to make it into a button.
However, when I forward the ref, I want to appropriately type it to be the type of whatever that element is. i.e.,
import React, { forwardRef } from 'react';
import Box from '../Box';
const Button = forwardRef((props, ref) => {
return <Box {...props} as="button" ref={ref} />;
});
If I took this one step further, ideally as could be anything that is accepted by styled.div() and know what the type of ref would be.
However, I can't find the appropriate way to type ref in a way that matches as expected.
How would this be done?
You can try something like:
interface ButtonProps = {
props: {}
}
const Button = forwardRef(({ ...props }: ButtonProps, ref?: React.Ref<HTMLButtonElement & HTMLAnchorElement>) => {
return <Box {...props} as="button" ref={ref} />;
});
I know that we can extend or add styling to existing components with styled-components like the Link component of react-router-dom. The said feature is indicated here. But, my problem is, how can I combine two or more existing components then add some more styles?
In my case, I have a reusable component for text elements like span, p and a with standard font-size, font-weight, etc. At the same time, I want to use the react-router-dom's Link component. Currently, I have something like this:
import styled from 'styled-components';
import { Link } from 'react-router-dom';
import { TextElement } from '../common';
/*
Can I do something like this?
const MyLink = styled(Link, TextElement)`
margin: 10px 0;
`;
or this?
const MyLink = styled([Link, TextElement])`
margin: 10px 0;
`;
*/
const MyPage = props => (
<>
<MyLink to="/next-page" />
</>
);
Any suggestion would be appreciated.
EDIT
My TextElement component is just something like this:
const Element = styled.span`
font-size: 13px;
font-weight: 500;
`;
// These styles are sample only. Actual styles depend on a "variant" prop.
// I did not show it here since it doesn't have to do with the question I'm asking.
export default ({ tag }) => (<Element as={tag} />);
You can use mixin for this using css helper.
Let's say you put the code for TextElement in a spaced mixin like:
import { css } from 'styled-components';
const spaced = css`
margin: 10px 0;
`;
And another mixin for Link
const styledLink = css`
color: blue;
`;
Then you can define your MyLink as:
import styled from 'styled-components'
const MyLink = styled(Link)`
${spaced}
${styledLink}
`;
The style component could be the wrapper of your custom component. For example:
Your style component:
export const CustomSpanWrapper = styled.div`
span {
...your-styles
}
`;
Your other component:
<CustomSpanWrapper>
<CustomSpan>
</CustomSpanWrapper>
I have created a Section component which will take in an image as a property and its children as content to be displayed within the section, so the component would look as follows...
<Section image={'path/to/image'}>
//content
</Section>
The component will take the image property and set it as a url for background-image style...
let sectionStyle = {
backgroundImage: `url(${this.props.image})`
}
which will then be processed in the return element...
return (
<section
style={this.props.image ? sectionStyle : null}>
<div>
{this.props.children}
</div>
</section>
)
My question is, is it possible to Lazyload the background image whilst also not compromising the contents availability for SEO? in other words i want to avoid LazyLoading the entire Section, but somehow LazyLoad just the image associated with the Section.
An updated version of #naoise-golden 's answer
import PropTypes from 'prop-types';
import React from 'react';
export default class LazyImage extends React.Component {
constructor (props) {
super(props);
this.state = {
src: null,
};
}
componentDidMount () {
const { src } = this.props;
console.log('LazyImage componentDidMount props:', this.props);
const imageLoader = new Image();
imageLoader.src = src;
imageLoader.onload = () => {
console.log('LazyImage loaded src:', src);
this.setState({ src });
};
}
render () {
const { placeholder, className, height, width, alt } = this.props;
const { src } = this.state;
return (
<img src={src || placeholder} className={className} height={height} width={width} alt={alt} />
);
}
}
LazyImage.propTypes = {
src: PropTypes.string,
placeholder: PropTypes.string,
className: PropTypes.string,
height: PropTypes.number,
width: PropTypes.number,
alt: PropTypes.string,
};
In order to defer loading background-image, you will need to create a stylesheet for the CSS properties that load any file with url, since you don't want these images to delay the first contentful paint.
For instance:
FirstModal.module.less
This file has the vital CSS properties that will be loaded first...
.styles {
&-container {
position: absolute;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
align-content: center;
justify-content: center;
align-items: center;
}
}
firstModalBackground.module.less
This file will load after the critical CSS...
.styles {
&-container {
background: url('../../images/code/code.jpg') no-repeat center center fixed;
background-size: cover;
}
}
For demonstration purposes, I will use React.Component here, but, if you want to try to optimize things, you can also use React.PureComponent (I tested it and everything worked fine).
firstModal.jsx
const classNames = require('classnames');
const React = require('react)';
const {stylesContainer} = require('./firstModal.module.less');
class FirstModal extends React.Component {
constructor(props) {
super(props);
this.state = {
classNames: classNames([stylesContainer])
};
}
async componentDidMount() {
const backgroundImage = await import(
'./firstModalBackground.module.less'
);
this.setState({
classNames: [
classNames(this.state.classNames, [backgroundImage.stylesContainer]),
]
});
}
render() {
// console.log(this.state.classNames);
return <div className={this.state.classNames}>It werks!</div>;
}
}
module.exports = FirstModal;
You could even take a step further, if you have a low resolution image that loads faster, you can have a "three-step background-image loading", for instance, on componentDidMount:
async componentDidMount() {
const lowResolutionBackgroundImage = await import(
'../greeting-page/firstModalLowResBackground.module.less'
);
const baseClass = this.state.classNames;
this.setState({
classNames: [
classNames(baseclass,
lowResolutionBackgroundImage.stylesContainer),
]
});
const backgroundImage = await import(
'./firstModalBackground.module.less'
);
this.setState({
classNames: [
classNames(baseClass,
backgroundImage.stylesContainer),
]
});
}
Here is a simple component to lazy load images:
class LazyImage extends React.Component {
state = { src: null };
componentDidMount() {
const { src } = this.props;
const imageLoader = new Image();
imageLoader.src = src;
imageLoader.onload = () => {
this.setState({ src });
};
}
render() {
return <img src={this.state.src || this.props.placeholder} />;
}
}
You would call it with <LazyImage src='path/to/hd.jpg' placeholder='path/to/placeholder.jpg' />
I created a library for lazy-loading images. It's main feature is to provide dynamic resizing of images, but it can also solve your problem. I recently PR'd in some changes for background image lazy loading.
https://github.com/peterlazzarino/react-adaptive-image
Here's an example you could use for lazy background image loading that will resolve images from imgur. In my production app my image resolver points at an image server, but this is totally customize-able. You will just need to style the .header-image class to have a height and width.
import React from 'react';
import ReactDOM from 'react-dom';
import { initImages } from 'react-adaptive-image';
import AdaptiveImage from 'react-adaptive-image';
initImages({
imageResolver: function(image){
return `https://i.imgur.com/${image.fileName}`
}
})
class App extends React.Component {
render() {
return (
<AdaptiveImage backgroundImage className="header-image" fileName={'U6zWkcZ.png'} />
);
}
}
ReactDOM.render(<App />, document.getElementById('react-root'));