Pass props into Higher order layout function in Next JS - javascript

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.

Related

Next.js default component value

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

Pass a function from a Functional Component to a Class based Component

Parent Component is like below
import React, { useState } from "react";
import EntityDefinition from "./EntityDefinition";
export default function EntitySelection(props) {
const testFun = () => {
console.log("Function activated");
};
return (
<>
<div>
<EntityDefinition
testFun={testFun}
/>{/**Calling a Class based Component*/}
</div>
</>
);
}
Class based Component (Child)
import React from "react";
import { ComboBox, DropdownOption, Button } from "react-widgets";
import axios from "axios";
export default class EntityDefinition extends React.Component {
render() {
return (
<React.Fragment>
<div>
{" "}
<Button onClick={this.testFun}>Close</Button>{" "} {/*/Calling the function passed*/}
</div>
</React.Fragment>
);
}
}
but when i clicked the button the testFun function is not being called.
i tried something like onClick={this.state.testFun} , onClick={testFun}
but nothing is happening.
can someone point what am i doing wrong here.
testFun is a prop. So use this.props.testFun
onClick={this.props.testFun}
testFun is a prop, and you are using the ES6 class component, and it receives the props as a props object, so you can try accessing it as below
onClick={this.props.testFun}
You need to refer to props passed to a class based components using this.props from inside the class based components:
Docs
In your case, you should change the onClick listener to this:
<Button onClick={this.props.testFun}>

TypeError: Cannot destructure property 'id' of 'this.props.Name' as it is undefined. In ReactJS

Could you pleases helping me fix in this problem.
TypeError: Cannot destructure property 'id' of 'this.props.Name' as it is undefined.
src/component/Detail.js file
import React, { Component } from 'react';
import { Character } from './Data_Character/Character';
import Total from './Total';
export default class Detail extends Component {
constructor(props) {
super(props);
this.state = {
names: Character
}
}
render() {
const { names } = this.state;
return(
<div>
{names.map(name => (
<Total key={name.id} Name={name} />
))};
</div>
)
}
}
src/component/Total.js file
import React, { Component } from 'react';
export default class Total extends Component {
render() {
const { id} = this.props.Name;
return(
<div>
{id}
</div>
)
}
}
src/App.js file
import React from 'react';
import './App.css';
import Footer from './component/Footer';
import Detail from './component/Page_ความเป็นมา/Detail';
function App() {
return (
<div className="App">
{/* <Navbar /> */}
{/* <Body /> */}
<Detail />
{/* <Detail_Home /> */}
<Footer />
</div>
);
}
export default App;
Provide a default value, or object, to destructure from const { id } = this.props.Name || {};.
This is even simpler if you convert your Total component to a functional component.
const Total = ({ Name: { id } = {} }) => <div>{id}</div>;
Most likely its because Character doesn't have an id field.
You are passing the contents of Character all the way through (via Name props), but the contents of Character must be omitting id.
You are iterating through the data received from 'Character' object. To do what you do, check if your 'Character' object is in the below format
Character = [{id: 'id1', name: 'Mark'}, {id: 'id2', name: 'John'}]
Character that you are importing in Detail.js must not having id as its property. Check that part or post Character's structure.

How to call an event handler function from App.js in two other components

I created a reset function in App.js and want to call it by an onclick in two other components. the problem is that it works in one component but doesn't in the other.
Here are the codes snippets
App.js
import React from 'react';
import Result from './components/Result';
import GeneralResult from './components/GeneralResult';
class App extends Component {
constructor(props) {
super(props);
this.state = {
result: '',
counter: 0,
}
}
// Reset function
handleReset=()=>{
this.setState({
result: '',
counter: 0,
)}
renderResult() {
return (
<div>
<Result reset={()=>this.handleReset()} />
<GeneralResult back={()=>this.handleReset()} />
</div>
);
}
Result.js
first component making use of reset()
function Result(props) {
return (
<div>
<span>
<button onClick={props.reset}>Replay</button>
</span>
</div>
);
}
export default Result;
GeneralResult.js
second component making use of the reset
import React, { Component } from 'react';
export default class GeneralResult extends Component {
render() {
return (
<React.Fragment>
<h2>Congratulations you won!</h2>
<span>
<button onClick={props.back}> Back to Question</button>
</span>
</React.Fragment>
);
}
}
You can pass the handler as props, and render the component from the parent class.
class Child extends Component {
render(){
return(
<button onClick = {this.props.onClick}></button>
)
}
}
export default Child;
import Child from 'path/to/child';
class Parent extends Component {
onClick = (e) => {
//do something
}
render () {
return(
<Child onClick = {onCLick}/>
)
}
}
Problem is that GeneralResult is class based component. so when you need to access props passed to it. you have to use this.props.
export default class GeneralResult extends Component {
render() {
return (
<React.Fragment>
<h2>Congratulations you won!</h2>
<span>
// you need to change "props.back"
// to "this.props.back"
<button onClick={this.props.back}> Back to Question</button>
</span>
</React.Fragment>
);
}
}

Pass React component as props to be used as a template to render nodes in a TreeView

I am building a flexible <TreeView /> component, where the user will be able to inject his own <TreeNode />, as an HTML template.
What I currently do works, but I can't set the propTypes as mandatory with my design pattern, because the <TreeNode /> component only receive its props when I use it in <TreeView />, in a map function.
Is there a better way to do this so I can use mandatory propTypes?
TreeNode.js:
const TreeNode = (props) => {
return (
<div>
<p>{props.id}</p>
<p>{props.nodeTitle}</p>
<p>{props.nodeDescription}</p>
</div>
)
}
TreeView.js, very simplified:
export class TreeView extends React.Component {
render() {
const childrenList = props.treeViewData.map((treeNodeData) => {
const treeNodeTemplateProps = {
id: treeNodeData.id,
nodeTitle: treeNodeData.nodeTitle,
nodeDescription: treeNodeData.nodeDescription
}
const treeNodeTemplate = this.props.treeNodeTemplate(treeNodeTemplateProps )
return (
{treeNodeTemplate}
)
})
return <div>{childrenList}</div>
}
}
index.js
import { TreeView } from './TreeView'
import { treeViewData } from './treeViewData'
import { TreeNode } from './TreeNode'
const element = <TreeView
treeViewData={treeViewData}
treeNodeTemplate={TreeNode} />
ReactDOM.render(element , document.getElementById('root'))
While treenode is a functional component you can provide propTypes to it as you would do with another component:
const TreeNode = (props) => {
return (
<div>
<p>{props.id}</p>
<p>{props.nodeTitle}</p>
<p>{props.nodeDescription}</p>
</div>
)
}
TreeNode.propTypes = {
title: PropTypes.string
};
// Same approach for defaultProps too
TreeNode.defaultProps = {
title: "No Title",
};
export default TreeNode;

Categories