Problem in higher order components in react - javascript

I'm learning how to use higher order components. There I want to highlight some text. In my code I can highlight the whole line by using <div>. The problem is I only want to highlight a part of the text. So I tried <span>. But when I use span the whole highlighting part doesn't work. Since it doesn't give any error I can't understand what where the error comes from.
HighlightedText.js
import React, { Component } from 'react';
import UpdatedComponent from './Hoc';
class HighlightedText extends Component {
render() {
return <h1>Highlighted Text</h1>
}
}
export default UpdatedComponent(HighlightedText);
Hoc.js
const UpdatedComponent = OriginalComponent => {
class NewComponent extends React.Component {
render() {
return(props) =>(
<div style={{ background: 'Yellow', padding: 2 }}>
<OriginalComponent {...props}/>
</div>
)
}
}
return NewComponent;
}
export default UpdatedComponent;

Issues
Your HOC looks to be trying to return a functional component from the render method of a class-based component.
Props aren't spread correctly.
padding: 2 may not be valid, it should probably provide a unit, whatever you need
Solution
To fix the highlighting I believe you just need to specify a display: inline-block; CSS rule to the div. Spread this.props from the class-based component to the wrapped component.
const updatedComponent = (OriginalComponent) => {
class NewComponent extends React.Component {
render() {
return (
<div
style={{
background: "Yellow",
padding: "1rem", // <-- provide unit, 1rem ~ 16px
display: "inline-block" // <-- inline-block display
}}
>
<OriginalComponent {...this.props} />
</div>
);
}
}
return NewComponent;
};

If you want to highlight just a part of the text, it requires modifying the virtual DOM tree returned by the original component, since you can't just easily wrap it like in your example. You might want to use react-string-replace to achieve this.

Related

React: Spring transition not behaving properly

I recently got started with both React and JavaScript. I've been reading the Spring API docs for the Transition component, and ended up with this piece of code when doing some tests. Now, I expected a smooth transition, but got the following sequence:
When switch changes, the mounted element abruptly appears.
Some time goes by (this is dependent on the config that I choose)
The one unmounted disappears (abruptly as well)
For what I read in the docs, I'm not able to see what I'm doing wrong here, so any help is more than welcome. I've tested different configs and always get that sequence.
PS: Content is just a plain div that takes its background color as prop, so nothing fancy there.
import React from 'react';
import './App.css';
import Content from './components/content';
import {Transition, config} from 'react-spring/renderprops';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
switch: false
};
}
render() {
return (
<div id="container" onClick={() => {this.setState({switch: !this.state.switch})}}>
<Transition
config={config.slow}
items={this.state.switch}
from={{opacity: 0}}
enter={{opacity: 1}}
leave={{opacity: 0}}>
{s => s ? () => <Content backgroundColor="rgb(37, 95, 142)"></Content>
: () => <Content backgroundColor="rgb(37, 142, 118)"></Content>}
</Transition>
</div>
);
}
}
export default App;
Thanks!
The Transtition changes the opacity in your example, but you never give the changed values to you rendered component. It missing the prop parameter in the arrow function. You should add this prop to the style property to your component.
For example:
props => (<Content props={props} backgroundColor="rgb(37, 95, 142)" />)
And the Component is something like this:
const Content = ({ backgroundColor, props }) => (
<animated.div style={{ backgroundColor, ...props }}>Content</animated.div>
);
And now you can see the opacity change.
I also used animated.div here and added the native poperty to Transition for better performance.
Here is my example:
https://codesandbox.io/s/ecstatic-wood-7p1ym?file=/src/App.js

How can I create new global css classes on the fly in React?

I’m using Draft.js for a project. I created a component that allows you to choose a font from a menu, then apply that font to the selected text in the Draft.js editor.
The way I need to do this:
Dynamically add a #font-face rule for the selected font that includes the font name and file location;
Create a new CSS class containing the font-family property that uses the font name from the #font-face rule that was added; and
Apply that new CSS class to the selection.
I’ve gotten everything but #2 figured out. For #2, I’m not finding any clear methods or tools to use that allow me to create new CSS classes in the global space. I’m reading a lot about css-in-js, but everything seems bloated or overly complex.
I’m formatting the #font-face rule and class like this:
const newClass = {
'#font-face': [
{
fontFamily: `${font.label} ${FONT_FAMILY_PREFIX}-A`,
src: `url(/fonts/${font.value}-TD-Space.woff)`,
fontWeight: 'normal',
fontStyle: 'normal',
}, {
fontFamily: `${font.label} ${FONT_FAMILY_PREFIX}-B`,
src: `url(/fonts/${font.value}-TD.woff)`,
fontWeight: 'normal',
fontStyle: 'normal',
},
],
[`.${font.value}`]: {
fontFamily: `${font.label} ${FONT_FAMILY_PREFIX}-A, ${font.label} ${FONT_FAMILY_PREFIX}-B`,
},
})
But I’m just not sure where to put it so it’s accessible across the app.
Does this require the help of some package or is there a simpler way I can manage it without?
The simplest way is probably to use React's Context feature (docs here)
Basically, you define a context like this:
// style-context.js
export const Styles = {
style1: {
fontFamily: '"Times New Roman", Times, serif',
fontSize: '12px'
},
style2: {
fontFamily: 'Arial, Helvetica, sans-serif',
fontSize: '16px'
},
};
export const StyleContext = React.createContext({
theme: styles.style1, // default value
changeTheme: (newTheme) => {}
}
);
Then, you wrap your App like this:
// App.js
import React, { Component } from 'react'
import { FontContext, styles } from './style-context'
class App extends Component {
constructor(props) {
super(props)
// Function to change the theme
this.chooseTheme = (newTheme) => {
this.setState({
theme: styles[newTheme]
})
}
// State includes the current theme and function to change it
this.state = {
theme: styles.style1,
chooseTheme: this.chooseTheme
}
}
render() {
return (
// Wrap with the context's Provider, and pass in the theme from
// the state. Those state values will be available from all
// children, no matter how deep.
<StyleContext.Provider value={this.state}>
<div className="App">
{/* components */}
</div>
</StyleContext.Provider>
);
}
}
There are two ways to use the context, then. First, you can reference it in any child component like this:
// SomeComponent.js
import React, { Component } from 'react'
import {StyleContext} from './style-context'
class SomeComponent extends Component {
render() {
return (
<div style={
this.context.theme
}>
{/* ... */}
</div>
)
}
}
SomeComponent.contextType = StyleContext
export default SomeComponent
Really simple. The other way to access it is like this, which is useful if you have multiple Contexts you're combining on a single Component:
// TestButton.js
import React, {Component} from 'react'
import {StyleContext} from './style-context'
class TestButton extends Component {
render() {
return (
<StyleContext.Consumer>
{
// The contents of the Consumer tag will receive the
// 'value' prop set at the Provider level, which is,
// in this case, the App state containing the current
// theme and function to change it
({theme, chooseTheme}) => (
<button onClick={() => {chooseTheme('style2')}} style={theme}>Change Font</button>
)
}
</StyleContext.Consumer>
)
}
}
export default TestButton
Doing that will mean you don't need to programmatically change the classes that different divs are given, nor need to generate CSS classes in the first place.
Also, one of my favorite tricks for assigning styles is to use Object.assign() to do so, like this:
...
<div style={Object.assign(
{ backgroundColor: 'red', fontSize: '16px' },
this.context.theme
)}>
...
That way, you can set default values, decoupling the Context from the Component, and allows the theme's style attributes to only override matching keys, combining the rest.

Styled-component object can't observe mobx changes

I would like an HOC generated by styled-components to re-render when one of its properties get changed. I'm using MobX for change detection.
This doesn't respond to changes, I think I understand why. The question is if there is a simple workaround to make it work.
const DemoComponent = observer(styled.div`
background-color: ${props => props.myObject.myObservableIsTrue ? 'red' :
'green'};
`);
It's hard to tell by this little snippet, but one of my guesses would be you are not injecting any store, so currently, no store is being connected to your component.
here's a simple example of how I used styled-components with mobx if it helps:
EDITED:
I've updated the code example.
Do you know the Container / Presentational pattern?
This was the missing link.
In order to keep your renders as little as possible
you need to separate your stateful component from each other.
Spread them across a Container component (aka Dumb Components)
This way you separate state concerns and render only the component with the changed state.
UPDATED:
https://codesandbox.io/s/zxx6o2pq3l
Sorry!!! A bit of a hack job, but attempt to bring your entire code inside the #inject("store") class:
import React from "react";
import { observer, inject } from "mobx-react";
import styled, { css } from "styled-components";
#inject("store")
#observer
export default class OtherComponent extends React.Component {
render() {
const MyWrapper = (store) => {
const Wrapper = styled.div`
border: 1px solid black;
display: flex;
justify-content: space-between;
color: ${({ color }) => color};
border: 2px solid ${({ color }) => color || "black"};
padding: 10px;
margin-bottom: 10px;
`;
return (
<Wrapper {...store}>
styled-component
<button onClick={store.changeColor}>change color</button>
</Wrapper>
);
}
const { store } = this.props;
return (
<div>
{
MyWrapper(store)
}
</div>
);
}
}
Mobx is actually read like this: #inject("store") #observer export default class...
So it really an extension of an extended component; only wrapped variables will apply!

Next.js custom class on body using _document.js

I'm using the example here
https://github.com/zeit/next.js#custom-document
and I want to change the custom_class on the body tag
I have tried passing props on calling components but nothing works. I want to add a 'dark' class based on some condition but I have no idea how to change this.
EDIT:
I'm not sure this is possible. After getting help from the nextjs slack channel I was told
"Pages get rendered client side with next/link
And body is not touched after server side rendering"
I'm going to try and wrap things in another tag that I generate and try and change that.
The cleanest solution I found is not declarative, but it works well:
import Head from "next/head"
class HeadElement extends React.Component {
componentDidMount() {
const {bodyClass} = this.props
document.querySelector("body").classList.add(bodyClass || "light")
}
render() {
return <Head>
{/* Whatever other stuff you're using in Head */}
</Head>
}
}
export default HeadElement
With this Head component you would pass in "dark" or "light" (following the question's example for light/dark themes) as the bodyClass prop from the page.
As of current Next (10) and React (17) versions, If you'd like to change the body class from a page, you can can do it like this:
// only example: maybe you'll want some logic before,
// and maybe pass a variable to classList.add()
useEffect( () => { document.querySelector("body").classList.add("home") } );
Please note that useEffect is a Hook, so it can be used only in modern function components, not class ones.
The useEffect Hook can be used instead of the 'old' componentDidMount LifeCycle.
https://reactjs.org/docs/hooks-effect.html
https://reactjs.org/docs/hooks-overview.html
https://reactjs.org/docs/components-and-props.html
The only way to directly access the body tag on Next js is via the _document.js file but this file is rendered server side as stated in the Documentation.
The work around I suggest is to access the body tag from the component directly. Example:
const handleClick = (e) => {
document.querySelector('body').classList.toggle('dark')
}
<div onClick={handleClick}>Toggle</div>
The solution I came up with for my situation where I don't need a lot of unique body classes was to create a component called BodyClass.js and import that component into my Layout.js component.
BodyClass.js
import { Component } from 'react';
class BodyClass extends Component {
componentDidMount() {
if (window.location.pathname == '/') {
this.setBodyClass('home');
} else if (window.location.pathname == '/locations') {
this.setBodyClass('locations');
}
}
setBodyClass(className) {
// remove other classes
document.body.className ='';
// assign new class
document.body.classList.add(className);
}
render() {
return null;
}
}
export default BodyClass;
Layout.js
import Header from './Header';
import Footer from './Footer';
import BodyClass from '../BodyClass';
const Layout = ({ children }) => {
return (
<React.Fragment>
<BodyClass />
<Header />
{children}
<Footer />
</React.Fragment>
)
}
export default Layout;
Unfortunately, next/head does not allow specifying body class like React Helmet does:
<Helmet>
<body className="foo"/>
</Helmet>
Luckily, you can use this.props.__NEXT_DATA__.props.pageProps inside _document.js to get access to the page props and use them to to set the class on the <body> element:
import Document, { Html, Head, Main, NextScript } from 'next/document';
export default class MyDocument extends Document {
static async getInitialProps(ctx) {
const initialProps = await Document.getInitialProps(ctx);
return { ...initialProps };
}
render() {
const pageProps = this.props?.__NEXT_DATA__?.props?.pageProps;
console.log('pageProps => ', pageProps);
return (
<Html>
<Head />
<body className={pageProps.bodyClassName}>
<Main />
<NextScript />
</body>
</Html>
);
}
}
More info here.
Directly, it's not possible. But with another way, you can use framework like tailwind and insert the class directly in your css.
Here an example using tailwind:
.dark {
background-color: black;
color: white;
}
body {
#apply dark
}
It's not exactly the answer you are looking for, but I was able to accomplish the same thing functionally by passing a prop through the component which then updates a class on a top-level element that wraps everything in my app and sits just under the . This way each page can have it's own unique class and operates the same way I'd use a body class.
Here is where I pass the prop through
<Layout page_title={meta_title} page_desc={meta_desc} main_class={'contact_page'}>
And here is where I use it in the Layout component:
<main className={props.main_class}>
<Header page_title={props.page_title} page_desc={props.page_desc} />
{props.children}
<Footer />
</main>
This is my CSS only solution:
(I first looked at https://stackoverflow.com/a/66358460/729221 but that felt too complex for changing a bit of styling.)
(This uses Tailwind CSS, but does not need to.)
index.css:
body:has(#__next .set-bg-indigo-50-on-body) {
#apply bg-indigo-50;
}
layout.tsx:
//…
return (
<div className="set-bg-indigo-50-on-body">{children}</div>
)
This will work, as long as that div is a direct parent of <div id="__next">. Otherwise you need to update the css :has-rule.

How to work with styled components in my react app?

I had trouble naming this question and it seems quite broad, so, forgive me oh moderators. I'm trying out styled components for the first time and trying to integrate it into my react app. I have the following so far:
import React from 'react';
import styled from 'styled-components';
const Heading = styled.h1`
background: red;
`;
class Heading extends React.Component {
render () {
return (
<Heading>
{this.props.title}
</Heading>
);
}
}
export default Heading;
So, just a normal class, but then I import styled components up top, define the const Heading, where I specify that a Heading really is just a styled h1. But I get an error stating that Heading is a duplicate declaration since I also say class Heading....
I'm obviously completely missing something here. All the examples online doesn't actually show how you also use this with React. I.e. where do I define my class, my constructor, set my state, etc.
Do I have to move the styled component into it's own file, i.e.:
import styled from 'styled-components';
const Heading = styled.h1`
background: red;
`;
export default Heading;
Then create a React component that will serve as a wrapper of sorts, e.g. 'HeadingWrapper':
import React from 'react';
import Heading from './Heading';
class HeadingWrapper extends React.Component {
render () {
return (
<Heading>
{this.props.title}
</Heading>
);
}
}
export default HeadingWrapper;
A bit of clarity on this would greatly be appreciated! Thanks :)
styled.h1`...` (for example) returns a React component that works just like <h1>. In other words, you use <h1> like this:
<h1>h1's children</h1>
...so when you do const Heading = styled.h1`...`;, you'll use <Heading> the same way:
<Heading>Heading's children</Heading>
If you want a component that behaves differently, e.g. one that uses the title prop instead of children, you'll need to define such a component, and it will need to have a different name than the Heading component you already defined.
For example:
const styled = window.styled.default;
const Heading = styled.h1`
background: red;
`;
const TitleHeading = ({title}) => <Heading>{title}</Heading>;
// ...or...
class StatefulTitleHeading extends React.Component {
render() {
return <Heading>{this.props.title}</Heading>;
}
}
ReactDOM.render(
<div>
<Heading>I'm Heading</Heading>
<TitleHeading title="I'm TitleHeading"/>
<StatefulTitleHeading title="I'm StatefulTitleHeading"/>
</div>,
document.getElementById('container')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<script src="https://unpkg.com/styled-components#1.4.3/dist/styled-components.js"></script>
<div id="container"></div>
Frankly, though, it makes more sense to just use the component returend by styled.h1 directly:
const Heading = styled.h1`...`;
export default Heading;
// ...then...
<Heading>Children go here</Heading>
The semantics of children are already clear, and using <Heading title="Children go here"/> instead detracts significantly from that.
Let me make this really simple for you.
Let's create one styled component for heading named 'Heading'
const Heading = styled.h1`
color: 'black';
font-size: 2rem;
`
and now you can use it like following.
<Heading>{this.props.title}</Heading>
How you manage to get the title prop as it's child is no concern of style component's. It only manages how that title looks. Styled component is not a container that maintains your app/business logic.
Now let's look at an example in it's entirety.
import styled from 'styled-components'
// Heading.js (Your styled component)
const Heading = styled.h1`
color: 'black';
font-size: 2rem;
`
export default Heading
and now your container that will use your styled component
// Header.jsx (Your container)
class Header extends Component {
componentWillReceiveProps(nextProps) {
// This your title that you receive as props
console.log(nextProps.title)
}
render() {
const { title } = this.props
return (
<div id="wrapper">
<Heading>{title}</Heading>
</div>
)
}
}
I hope that helps. Let me know if you need further clarification.

Categories

Resources