ReactJS - Disabling a component - javascript

I need to disable PostList component in its initial state.
import React from 'react';
import PostList from './PostList';
const App = () => {
return (
<div className="ui container">
<PostList />
</div>
);
};
export default App;
Whats the best way to disable (and grey out) a component? Possible solutions are to pass a value as props and then apply it to a ui element, However please keep in mind that PostList may have inner nested components as well. Please share an example.

Since you mentioned in a comment that instead of hiding it, you want to grey it instead. I would use the disabled state and style the component. Since PostList could be nested, we don't know what the props are since you did not specify them.
Also, I assuming that you are not using styled-components.
import React, { useState } from "react";
import PostList from "./PostList";
const App = () => {
const [disabled, setDisabled] = useState(true);
return (
<div className="ui container">
<PostList
style={{
opacity: disabled ? 0.25 : 1,
pointerEvents: disabled ? "none" : "initial"
}}
/>
</div>
);
};
export default App;

There are 2 different ways I like to do something like this.
One way you can do it is by using state
this.state = {
showList: false
}
and than something like
return (
{this.state.showList && <PostList />}
)
Another option is to pass the showList in state as a prop, so something like
return(
<PostList show = {this.state.showList} />
)
and than in PostList something like
return props.show && (your component here)
You can also use conditional classNames, so if you want that component shown, you can throw a className and style it how you normally would, but if not, just throw a display: none. I usually save doing that for replacing a navbar with a dropdown button on smaller screens, but it is another option

Related

How to import component as a button in Nextjs?

I have this component with an internal link which is used on multiple pages
const index = ({
as: Component,
children,
fullWidth,
secondary,
widget,
...props
}) => {
return (
<Component
{...props}
className={classNames(`${styles.BetTrackerCTA} btn btn-primary`, {
[styles["BetTrackerCTA--full-width"]]: fullWidth,
[styles["BetTrackerCTA--secondary"]]: secondary,
[styles["BetTrackerCTA--widget"]]: widget,
})}
>
{children}
</Component>
);
};
export default index;
when I try to import it as a button in the Nextjs application I get this result, it renders both the styled anchor link and the button in the background
<div className={`${styles.StrategyForm__submit} mt-5`}>
<BetTrackerCTA
as="button"
type="submit"
fullWidth={isMobile}
disabled={!isFormDirty}
>
{t("update_strategy")}
</BetTrackerCTA>
</div>
With the same code in the React application I get the desired result
It looks like the prop "as" for some reason is rendering the component as a button and also the anchor inside the component, which is not happening in the regular React version
I suspect you have different styling in "the regular React version".
Your component will always render <a> because that's hardcoded. If you want to use <a> as a default component to render, you can define a default value for as property and you don't need nested <a>.
For example:
const Comp=({as: Component = 'a', ...rest}) => {
return <Component {...rest}>Foobar</Component>
}
then following usage will render <a> and <button>:
<Comp />
<Comp as="button" />

How to call returned const from a react function in a class based component

Here I have a Loading screen as a functional react component that I try to render conditionally in the App component.
The concept of this loading screen is that I have a boolean variable that will be used to conditionally render the home page after the loading screen ends.
import React from 'react';
import { useState, useEffect } from 'react';
import { useSpring, animated } from 'react-spring';
import BarLoader from 'react-spinners/BarLoader';
import Logo from "../assets/images/logo.svg";
const LoadingScreen = () => {
const spinner = `
display: block;
margin: 0 auto;
width: 150px;
height: 2.5px;
`;
const style = useSpring({opacity: 1, from: {opacity: 0}});
const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
setIsLoading(true);
setTimeout(() => {
setIsLoading(false)
}, 4000)
}, [])
const LoadingTemplate = () => {
<animated.div className="loading-screen" style={style}>
<div className="loader-wrapper">
<img className="splash-logo" src={Logo} alt="Marouane Edghoughi" />
<BarLoader color="#384BEB" css={ spinner } loading={isLoading} />
</div>
</animated.div>
}
return { LoadingTemplate, isLoading }
}
export default LoadingScreen;
When I try to call the boolean variable and the screen template in the following code:
render() {
const {LoadingTemplate, isLoading} = LoadingScreen();
return (
<Router>
{
isLoading ?
<LoadingTemplate />
:
<Container className="theme p-0" fluid={true}>
{/* something will be displayed here */}
</Container>
}
</Router>
);
}
}
I just get this error:
Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
You might have mismatching versions of React and the renderer (such as React DOM)
You might be breaking the Rules of Hooks
You might have more than one copy of React in the same app
See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.
The function is working properly if I try to call it from a functional component. This is my first time trying it with a class.
Any help would be greatly appreciated ^_^
The error message is 100% correct. Hooks can be used only in Function Components, and cannot be used like this in class components. The underlying mechanics of the two types are different. Hooks are a feature of functional components and rely on those mechanics, and are therefore not compatible with class components.
You may not realize that you are using a hook, but LoadingScreen actually is one: It returns a value other than a React Element, and you are calling it as a function (i.e. const x = LoadingScreen()), rather than using it as a component (i.e. <LoadingScreen />).
That's definitely not allowed in class components. You could use a function component instead:
const Component = () => {
const {LoadingTemplate, isLoading} = LoadingScreen();
return (
<Router>
{
isLoading ?
<LoadingTemplate />
:
<Container className="theme p-0" fluid={true}>
{/* something will be displayed here */}
</Container>
}
</Router>
);
}
}
Or you can try these methods to get around this limitation. If you do decide to use a function component instead, then you should use useLoadingScreen to follow the React hook naming conventions.

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

What is the best way to go about conditional rendering in the case of very similar components?

I render different landing pages based on whether the user is a professor, student, or not logged in. The landing pages are very similar; the only difference is the buttons displayed. I know I can go around this using inline conditions or simple if-else statements. However, I was wondering what the best practices are to implement conditional rendering in this case. I know higher order components (HOCs) can help but I was not sure if they are overkill in this particular case.
To be on the same page, here are the different Landing components that I currently render using if-else statements.
Landing.js (unlogged users):
import React from 'react';
import { withRouter } from 'react-router-dom';
import { compose } from 'recompose';
import { withEither } from '../../helpers/withEither';
import LandingStudent from './LandingStudent';
import LandingProfessor from './LandingProfessor';
import './Landing.css';
const Landing = ({ history }) => {
return(
<div className="header">
<div className="text-box">
<h1 className="header-primary">
<span className="header-primary-main">
QME
</span>
<span className="header-primary-sub">
the best way to ask questions
</span>
</h1>
</div>
</div>
);
};
export default Landing;
LandingProfessor.js
import React from 'react';
import { withRouter } from 'react-router-dom';
import RaisedButton from 'material-ui/RaisedButton';
import './Landing.css';
const LandingProfessor = ({ history }) => {
return(
<div className="header">
<div className="text-box">
<h1 className="header-primary">
<span className="header-primary-main">
QME
</span>
<span className="header-primary-sub">
the best way to ask questions
</span>
</h1>
<RaisedButton
className="btn-animated btn-landing"
label="Create Class"
onClick={() => history.push('/courses/new')}
/>
<RaisedButton
className="btn-animated btn-landing"
label="Dashboard"
onClick={() => history.push('/courses')}
/>
</div>
</div>
);
};
export default withRouter(LandingProfessor);
LandingStudent.js
import React from 'react';
import { withRouter } from 'react-router-dom';
import RaisedButton from 'material-ui/RaisedButton';
import './Landing.css';
const Landing = ({ history }) => {
return(
<div className="header">
<div className="text-box">
<h1 className="header-primary">
<span className="header-primary-main">
QME
</span>
<span className="header-primary-sub">
the best way to ask questions
</span>
</h1>
<RaisedButton
className="btn-animated btn"
label="Join Class"
onClick={() => history.push('/courses/join')}
/>
</div>
</div>
);
};
export default withRouter(Landing);
A 'trick' could be to append a className on the root div for landscape, namely 'student' 'professor' or 'logout' and use css display: none to hide unwanted items in each scenarios
Another approach would be to keep your 3 components but delegate all the rendering to a common 'LandingRenderer' component that would accept boolean properties like 'showJoinClassButton' 'showCreateButtonButton' etc.
Then your 3 components render would look like domething like this
LandingProfessor: (props) => (
<LandingRenderer
showJoinClassButton={false}
showCreateClassButton={true}
...
{...props} />
)
I would opt for composition over inheritance here, meaning create many reusable components and compose your top-level components with those reusable ones rather than make your top-level components inherit from one common component type. If you follow the latter approach, you might end up with a laundry list of props that I argue is questionably better than what you have right now.
To start, you could componentize your header:
Header.js
export default function Header(props) {
return (
<h1 className="header-primary">
{props.children}
</h1>
);
}
Header.Main = function HeaderMain(props) {
return (
<span className="header-primary-main">
{props.children}
</span>
);
};
Header.Sub = function HeaderSub(props) {
return (
<span className="header-primary-sub">
{props.children}
</span>
);
};
and use it in Landing.js:
import Header from './Header.js';
const Landing = ({ history }) => {
return(
<div className="header">
<div className="text-box">
<Header>
<Header.Main>QME</Header.Main>
<Header.Sub>the best way to ask questions</Header.Sub>
</Header>
</div>
</div>
);
}
I don't think hoc in this particular case is an overkill.
I think whenever you can use hoc for better readability and useability, use it.
Using some of recompose hocs (you should install recompose: npm install recompose)
export const renderByConditions = (...conditions) => compose(
...conditions.map(condition =>
branch(condition[0], (condition[1] && renderComponent(condition[1])) || renderNothing, condition[2] && renderComponent(condition[2]))
)
)
You should pass arrays with the signature:
[condition, left (if the condition is true), right (else)]
condition - (ownProps) => youCondition
left - Component | empty
right - Component | empty
when left is empty - if the condition is true, it will render null.
when right is empty - if the condition is not true, it will render the component we wrapped
Then you can use the hoc:
renderByConditions(
[props => props.landing, props => <Landing {...props}/>],
[props => props.proffesor, LandingProfessor],
[props => props.student, LandingStudent],
[Component => Component, DefaultComponent]
)
I would reccommend start using recompose hocs, they are great!

React functional props

Let me show you an example first:
<List hidden={this.state.hideList} />
For this case I would like hidden prop to function in the following way: if value is true – hide component or show otherwise. And List shouldn't be aware about this prop.
I don't know much about React internals, but it seems to be possible. We just need to extended React.Component class. Maybe extending globally with all functional props isn't good idea, but having something like this at the top would be nice:
import { ReactComponent } from 'react'
import extend, { hidden } from 'react-functional-props'
const Component = extend(ReactComponent, hidden)
It reminds me directives in Angular.js, but here we are able to combine them.
You can just return null or undefined in the render method to prevent it from rendering, or use a conditional class to hide it, like this:
return !this.state.hideList && <List />;
And about the
react-functional-props
In React, you can use higher order component (HOC) to achieve what you need, for example:
const Hideable = Component => (props) => {
const { isHidden, ...otherProps } = props;
return !isHidden && (<Component {...otherProps} />);
};
const HideableList = Hideable(List);
...
return (
<div>
<HideableList isHidden={this.state.hideList} /> // The isHidden props is handled by the HOC
</div>
);
But for a simple use case like this, I think just handle the hide and show in the parent component is much more clearer.

Categories

Resources