Next.js default component value - javascript

I am trying to create a global variable that all components are rendered with by default and set that default value but I'm not sure how to do the 2nd part. Here's what I have so far in my _app.tsx:
import { AppProps } from "next/app";
import type { NextComponentType } from 'next'
import Blue from "../components/blue";
type CProps = AppProps & {
Component: NextComponentType & {model?: string }
};
const MyApp = ({
Component,
pageProps: { ...pageProps },
}: CProps) => {
return (
<>
{Component.model === 'blue' ? (
<Blue>
<Component {...pageProps} />
</Blue>
) : (
<Component {...pageProps} />
)}
</>
);
};
But this obviously doesn't give me a default value for model. It just creates that variable with null value for all the components. How do I set the value?
Side question: Is this better done using React Context?
Edit 1:
This is how the component sets the model value if it does not want to use the default value:
const ComponentFoo = () => {
return (
<>Test</>
);
};
ComponentFoo.model = 'red'
export default ComponentFoo;

This sounds like a good candidate for Next.js Layouts. You would have to compose a Layout component, similar to Blue in your example, which accepts a color prop and encapsulates the color rendering logic to the layout file. You can implement a default render path if no color prop is provided.
Then you can use it like so:
// pages/whatever.tsx
import type { ReactElement } from 'react'
import Layout from '../components/layout'
export default function Page() {
return {
/** Your content */
}
}
Page.getLayout = function getLayout(page: ReactElement) {
return (
<Layout color="blue">
{page}
</Layout>
)
}
https://nextjs.org/docs/basic-features/layouts#with-typescript

Related

Pass props into Higher order layout function in Next JS

I have this layout component as Higher Order Component:
import PropTypes from 'prop-types';
Layout.propTypes = {
children: PropTypes.node.isRequired,
type: PropTypes.string.isRequired
};
function Layout({ children, type }) {
return (
<div>
{children}
{type}
</div>
);
}
export function withLayout(Component) {
Component.Layout = Layout;
return Component;
}
I am using it with another component like this:
import Layout from './
function ChildElement() {
return (
<>
This is the child element
</>
);
}
export default withLayout(ChildElement);
How can I pass the type prop into from withLayout(ChildElement)?
I have tried to pass in the type prop into it by passing a prop to <ChildElement type="Hello" /> but that will only work in <ChildElement /> and not withLayout() How do I make it work?
Thanks in advance.

TypeScript error Parameter 'props' implicitly has an 'any' type

I am testing out how to use Context in React but for some reason I cant run the app due the typescript error I get!
Code:
import React from 'react';
import './App.css';
class App extends React.Component {
render() {
return <Toolbar theme="dark" />;
}
}
function Toolbar(props) {
// The Toolbar component must take an extra "theme" prop
// and pass it to the ThemedButton. This can become painful
// if every single button in the app needs to know the theme
// because it would have to be passed through all components.
return (
<div>
<ThemedButton theme={props.theme} />
</div>
);
}
class ThemedButton extends React.Component {
render() {
return <button className={'btn btn-' + this.props.theme}></button>;
}
}
export default App;
Error I get:
C:/Users/as/Desktop/React - Mobx -Hooks/react-hooks-mobx/src/App.tsx
TypeScript error in C:/Users/iluli/Desktop/React - Mobx -Hooks/react-hooks-mobx/src/App.tsx(11,20):
Parameter 'props' implicitly has an 'any' type. TS7006
9 | }
10 |
> 11 | function Toolbar(props) {
Any idea how to fix this and explain the reason why it throws that error?
You just need to provide the Type for it:
import React from "react";
import "./App.css";
interface IToolbarProps {
theme: string;
}
class App extends React.Component {
render() {
return <Toolbar theme="dark" />;
}
}
// Here in the function
function Toolbar(props: IToolbarProps) {
// The Toolbar component must take an extra "theme" prop
// and pass it to the ThemedButton. This can become painful
// if every single button in the app needs to know the theme
// because it would have to be passed through all components.
return (
<div>
<ThemedButton theme={props.theme} />
</div>
);
}
// HERE as a generic
class ThemedButton extends React.Component<IToolbarProps> {
render() {
return <button className={"btn btn-" + this.props.theme}></button>;
}
}
export default App;

How to get two or more components to listen to eachother and turn off when others turn on

I have three components in my React app, all set to grey but can turn a different color when clicked, and the idea is to have the other components change back to grey whenever one of the components turns from grey to it's chosen color. Another way to phrase it is that I want only one components to show it's color at a time.
Here is the index.js for my page.
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
import Redlight from "./Components/Redlight";
import Yellowlight from "./Components/Yellowlight";
import Greenlight from "./Components/Greenlight";
// probably add "isActive" line here? need to
// figure out how to get the components to listen
// to eachother
function App() {
return (
<div className="App">
<Redlight />
<Yellowlight />
<Greenlight />
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
And here is one of the components. All the components are written the exact same way, so consider this one to count as all three besides the obvious differences of color.
import React from "react";
class Redlight extends React.Component {
state = {
className: "Offlight"
};
handleClick = e => {
e.preventDefault();
console.log("The red light was clicked.");
const currentState = this.state.className;
this.setState({
className: !currentState
});
};
render() {
return (
<div
className={this.state.className ? "Offlight" : "Redlight"}
onClick={this.handleClick}
/>
);
}
}
export default Redlight;
So far all the components show up and change color on click, but it's the matter of getting them all to listen to each other that is really hanging me up.
Try moving your state to your root component, which in your case is App, then make App stateful and Redlight, Bluelight, and Greenlight stateless.
If you turn your App component into a stateful component, you can pass the state of the lights down to the children component while at the same time you can manage their states in the parent's component level.
For example, for your App.js, do something like:
import React, { Component } from "react";
import Redlight from "./Redlight";
import Yellowlight from "./Yellowlight";
import Greenlight from "./Greenlight";
import "./App.css";
class App extends Component {
state = {
red: "Offlight",
yellow: "Offlight",
green: "Offlight"
};
clickHandler = (light, e) => {
e.preventDefault();
console.log("The " + light + " light was clicked.");
const currentState = this.state[light];
const newState = {
red: "Offlight",
yellow: "Offlight",
green: "Offlight"
};
this.setState({
...newState,
[light]: !currentState
});
};
render() {
return (
<div className="App">
<Redlight light={this.state.red} clicked={this.clickHandler} />
<Yellowlight light={this.state.yellow} clicked={this.clickHandler} />
<Greenlight light={this.state.green} clicked={this.clickHandler} />
</div>
);
}
}
export default App;
If you see, the state in the parent is controlling the class name for the lights, and the clickHandler turns all of them off, them turns the clicked one on.
Your children components can be cleaner, like this:
Yellowlight.js
import React from "react";
class Yellowlight extends React.Component {
render() {
return (
<div
className={this.props.light ? "Offlight" : "Yellowlight"}
onClick={(e) => this.props.clicked('yellow', e)}
/>
);
}
}
export default Yellowlight;
Redlight.js
import React from "react";
class Redlight extends React.Component {
render() {
return (
<div
className={this.props.light ? "Offlight" : "Redlight"}
onClick={(e) => this.props.clicked('red', e)}
/>
);
}
}
export default Redlight;
Greenlight.js:
import React from "react";
class Greenlight extends React.Component {
render() {
return (
<div
className={this.props.light ? "Offlight" : "Greenlight"}
onClick={(e) => this.props.clicked('green', e)}
/>
);
}
}
export default Greenlight;
You can check the final code in this sandbox where I tried to replicate your problem: https://codesandbox.io/s/friendly-haibt-i4nck
I suppose you are looking for something like this
What you need, is to lift up the state in order to hold the selected color/light into it. For this example, I'm using the state hook instead of the class component approach. I've also created one <Light /> component as they share the same logic.
You can find inline comments of what is going on for each of the steps.
Let me know if you find any trouble reading or implementing it.
What you want to do is to lift the state up to a common ancestor. You can create a common ancestor to host the information that all 3 light components need. You can pass a boolean isLightOn to tell the component to switch on or off. You will also pass an event handler to allow a light component to set the light color.
const LightContainer = () => {
const [lightColor, setLightColor] = useState('');
const RED_COLOR = 'RED';
const YELLOW_COLOR = 'YELLOW';
const GREEN_COLOR = 'GREEN';
return (
<div>
<Light className="Redlight" isLightOn={lightColor === RED_COLOR} onLightSwitch={() => { setLightColor(RED_COLOR); }} />
<Light className="Yellowlight" isLightOn={lightColor === YELLOW_COLOR} onLightSwitch={() => { setLightColor(YELLOW_COLOR); }} />
<Light className="Greenlight" isLightOn={lightColor === GREEN_COLOR} onLightSwitch={() => { setLightColor(GREEN_COLOR); }} />
</div>
);
};
On your light components, we can also make it more generic since we move most of the logic to the parent component.
const Light = ({ isLightOn, onLightSwitch, className }) => {
return (
<div
className={isLightOn ? 'Offlight' : className}
onClick={onLightSwitch}
/>
);
};

How to use #material-ui/core/useScrollTrigger in Next.js?

I am studying the documentation Material-UI useScrollTrigger and trying to apply it to Next to repeat the Elevate App Bar.
https://material-ui.com/components/app-bar/#usescrolltrigger-options-trigger
import React from "react";
import AppBar from "#material-ui/core/AppBar";
import useScrollTrigger from "#material-ui/core/useScrollTrigger";
interface Props {
children: React.ReactElement;
}
function ElevationScroll(props: Props) {
const children = props;
const trigger = useScrollTrigger({
disableHysteresis: true,
threshold: 0
});
return React.cloneElement(children, {
elevation: trigger ? 4 : 0
});
}
export default class HeaderAppBar {
render() {
return (
<ElevationScroll {...props}>
<AppBar />
</ElevationScroll {...props}>
);
}
}
But I get the error ReferenceError: props is not defined. Please help solve the problem.
HeaderAppBar is a class component, so you need to refer to this.props instead of just props.
Also, you’ll need to destructure children off of props in ElevationScroll:
const { children } = props

In React 16.4.0, how is using the new context syntax better than just exporting an object literal?

In React 16.4.0, why use the in-built Context component, when you can accomplish the same thing using something like an object literal you import to whoever that needs it?
In Facebook's example (https://reactjs.org/docs/context.html#examples), the theme-context.js file can essential pass the object literal directly rather than use ThemeContext. The app.js code can read theme-context exported object literal and pass it's value as props to them-button.js. Using context component seems unnecessary. Here is the code taken from Facebook's tutorial:
theme-context.js
export const themes = {
light: {
foreground: '#000000',
background: '#eeeeee',
},
dark: {
foreground: '#ffffff',
background: '#222222',
},
};
export const ThemeContext = React.createContext(
themes.dark // default value
);
themed-button.js
import {ThemeContext} from './theme-context';
function ThemedButton(props) {
return (
<ThemeContext.Consumer>
{theme => (
<button
{...props}
style={{backgroundColor: theme.background}}
/>
)}
</ThemeContext.Consumer>
);
}
export default ThemedButton;
app.js
import {ThemeContext, themes} from './theme-context';
import ThemedButton from './themed-button';
// An intermediate component that uses the ThemedButton
function Toolbar(props) {
return (
<ThemedButton onClick={props.changeTheme}>
Change Theme
</ThemedButton>
);
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
theme: themes.light,
};
this.toggleTheme = () => {
this.setState(state => ({
theme:
state.theme === themes.dark
? themes.light
: themes.dark,
}));
};
}
render() {
// The ThemedButton button inside the ThemeProvider
// uses the theme from state while the one outside uses
// the default dark theme
return (
<Page>
<ThemeContext.Provider value={this.state.theme}>
<Toolbar changeTheme={this.toggleTheme} />
</ThemeContext.Provider>
<Section>
<ThemedButton />
</Section>
</Page>
);
}
}
ReactDOM.render(<App />, document.root);
One thing you missed is that the change in context will ineffect change the value received at Consumer there by initiating a rerender which cannot be achieved by importing the value.

Categories