Reactjs 如果组件未包含在特定父组件中,则显示警告
解决方案: 我正在创建一个组件,我只想在组件的子级或非子级时显示警告 我已经做了一件事来解决这个问题,但我不满意,我认为这是丑陋的Reactjs 如果组件未包含在特定父组件中,则显示警告,reactjs,Reactjs,解决方案: 我正在创建一个组件,我只想在组件的子级或非子级时显示警告 我已经做了一件事来解决这个问题,但我不满意,我认为这是丑陋的 export const CardBody = ({ children, isInCard }) => { if (!isInCard) console.warning('Not in card') return <div className="cardbody">{children}</div> }
export const CardBody = ({ children, isInCard }) => {
if (!isInCard)
console.warning('Not in card')
return <div className="cardbody">{children}</div>
}
export const CardHeader = ({ children, isInCard }) => {
if (!isInCard)
console.warning('Not in card')
return <div className="cardheader">{children}</div>
}
export const CardBodyContainer = ({ children, isInCard }) => {
if (!isInCard)
console.warning('Not in card')
return <div className="cardbodycontainer">{children}</div>
}
export const Card = ({ children }) => {
if (children.length > 0) {
children = children.map((child, index) => {
if (
[
CardHeader.displayName,
CardBody.displayName,
CardBodyContainer.displayName,
].includes(child.type.displayName)
) {
return React.cloneElement(child, {
...child.props,
key: index,
isInCard: true,
});
}
return child;
});
}
return <div>{children}</div>
};
export const CardBody=({children,isInCard})=>{
如果(!isInCard)
控制台。警告('不在卡中')
返回{children}
}
export const CardHeader=({children,isInCard})=>{
如果(!isInCard)
控制台。警告('不在卡中')
返回{children}
}
export const CardBodyContainer=({children,isInCard})=>{
如果(!isInCard)
控制台。警告('不在卡中')
返回{children}
}
导出常量卡=({children})=>{
如果(children.length>0){
children=children.map((子,索引)=>{
如果(
[
CardHeader.displayName,
CardBody.displayName,
CardBodyContainer.displayName,
].includes(child.type.displayName)
) {
返回React.cloneElement(子级{
…儿童道具,
关键词:索引,,
伊辛卡德:没错,
});
}
返回儿童;
});
}
返回{children}
};
这是与以下方面合作的:
<Card>
<CardBody/>
</Card>
但它不适用于:
// displays warning even CardBody is in Card
<Card>
<div>
<CardBody/>
</div>
</Card>
//即使卡体在卡中也会显示警告
简短解释
简言之,你在孩子们的内心深处只有一个层次。您必须递归遍历子对象的子对象以指定isInCard
属性
长篇大论
React将其子对象组合为一棵树:
(2) ["Card", Object]
0: "Card"
1: Object
type: ƒ CardBodyContainer() {}
key: null
ref: null
props: Object
children: Array(2)
0: "Body Container"
1: Object
type: "div"
key: null
ref: null
props: Object
children: Array(2)
0: Object
1: Object
_owner: FiberNode
_store: Object
_owner: FiberNode
_store: Object
其中儿童
可能有自己的儿童
和自己的道具
。因此,您必须递归遍历所有子项
,并有条件地为它们分配道具
。不幸的是,您无法将属性分配给所有子项,因为有时这些子项可能是HtmleElement或string/boolean/number。因此,您不能/不应该将道具分配给非反应元素
演示
代码
Example.js
import * as React from "react";
export const CardBody = ({ children, isInCard }) => {
if (!isInCard) {
console.error("CardBody was not used within a Card component");
return <div className="error">Invalid CardBody</div>;
}
return <div className="cardbody">{children}</div>;
};
export const CardHeader = ({ children, isInCard }) => {
if (!isInCard) {
console.error("CardHeader was not used within a Card component");
return <div className="error">Invalid CardHeader</div>;
}
return <div className="cardheader">{children}</div>;
};
const CardBodyContainerComponent = ({ children }) => (
<div className="cardbodycontainer">{children}</div>
);
/**
* A HOC that checks if a Card Component contains an ```isInCard``` property
*
* @function withCardCheck
* @param Component - a React component
* @param type - a string of the Card component type that was used
* @returns {ReactElement}
* @example withCardCheck(Component, "example")
*/
const withCardCheck = (Component, type) => {
const wrappedComponent = (props) => {
if (!props.isInCard) {
console.error(`${type} was not used within a Card component`);
// throw Error(`${type} was not used within a Card component`);
return <div className="error">Invalid {type}</div>;
}
return <Component {...props} />;
};
return wrappedComponent;
};
export const CardBodyContainer = withCardCheck(CardBodyContainerComponent, "CardBodyContainer"
);
/**
* Checks if the child is a function (React functional component) and
* conditionally assigns it an ```isInCard``` property
*
* @function childContainsCardElements
* @param child - a React child component
* @returns {Object} either empty object or { isInCard: true }
* @example childContainsCardElements(element)
*/
const childContainsCardElements = (child) =>
typeof child.type === "function" &&
[
CardHeader.displayName,
CardBody.displayName,
CardBodyContainer.displayName
].includes(child.type.displayName)
? { isInCard: true }
: {};
/**
* Recursively iterates through children
*
* @function recursiveMap
* @returns {ReactElement}
* @example recursiveMap(children)
*/
const recursiveMap = (children) =>
React.Children.map(children, (child) => {
// checks if the child is a valid React element
if (!React.isValidElement(child)) return child;
// checks if child have their own children
if (child.props.children) {
// if so, then conditionally assign it an "isInCard" prop
child = React.cloneElement(child, {
...childContainsCardElements(child),
children: recursiveMap(child.props.children)
});
}
return child;
});
export const Card = ({ children }) => <div>{recursiveMap(children)}</div>;
import { Card, CardHeader, CardBody, CardBodyContainer } from "./Example";
import "./styles.css";
const App = () => (
<div className="App">
<h2 className="success">Valid</h2>
<Card>Card</Card>
<div className="divider" />
<h2 className="success">Valid</h2>
<Card>
Card
<CardBody>CardBody</CardBody>
</Card>
<div className="divider" />
<h2 className="success">Valid</h2>
<Card>
Card
<CardBodyContainer>
CardBodyContainer
<div>
<CardHeader>CardHeader</CardHeader>
<div>
<CardBody>CardBody</CardBody>
</div>
</div>
</CardBodyContainer>
</Card>
<div className="divider" />
<h2 className="error">Error</h2>
<CardBodyContainer>CardBodyContainer</CardBodyContainer>
<div className="divider" />
<h2 className="error">Error</h2>
<CardBody>Body</CardBody>
<div className="divider" />
<h2 className="error">Error</h2>
<CardHeader>Header</CardHeader>
<div className="divider" />
</div>
);
export default App;
import*as React from“React”;
export const CardBody=({children,isInCard})=>{
如果(!isInCard){
控制台。错误(“卡体未在卡组件中使用”);
返回无效的卡体;
}
返回{children};
};
export const CardHeader=({children,isInCard})=>{
如果(!isInCard){
console.错误(“卡头未在卡组件中使用”);
返回无效的卡头;
}
返回{children};
};
const CardBodyContainerComponent=({children})=>(
{儿童}
);
/**
*检查卡组件是否包含``isInCard``属性的HOC
*
*@具有CardCheck的函数
*@param组件-反应组件
*@param type-使用的卡组件类型的字符串
*@返回{ReactElement}
*@example with cardcheck(组件,“示例”)
*/
const withCardCheck=(组件,类型)=>{
常量wrappedComponent=(道具)=>{
如果(!props.isInCard){
错误(`${type}未在卡组件`)中使用);
//抛出错误(`${type}未在卡组件`)中使用);
返回无效的{type};
}
返回;
};
返回wrappedComponent;
};
export const CardBodyContainer=带CardCheck(CardBodyContainer组件,“CardBodyContainer”
);
/**
*检查子项是否为函数(反应功能组件),以及
*有条件地为其分配一个``isInCard``属性
*
*@function-childescardelements
*@param child-反应子组件
*@返回{Object}空对象或{isInCard:true}
*@示例ChildContainesCardelements(元素)
*/
const childContainsCardElements=(child)=>
typeof child.type==“函数”&&
[
CardHeader.displayName,
CardBody.displayName,
CardBodyContainer.displayName
].includes(child.type.displayName)
? {isInCard:true}
: {};
/**
*递归地遍历子对象
*
*@函数递归映射
*@返回{ReactElement}
*@example recursiveMap(子级)
*/
常量递归映射=(子项)=>
React.Children.map(Children,(child)=>{
//检查子元素是否为有效的React元素
如果(!React.isValidElement(child))返回子项;
//检查孩子是否有自己的孩子
if(child.props.children){
//如果是,则有条件地将其指定为“isInCard”属性
child=React.cloneElement(child{
…儿童(儿童),
子对象:递归映射(child.props.children)
});
}
返回儿童;
});
导出常量卡=({children})=>{recursiveMap(children)};
App.js
import * as React from "react";
export const CardBody = ({ children, isInCard }) => {
if (!isInCard) {
console.error("CardBody was not used within a Card component");
return <div className="error">Invalid CardBody</div>;
}
return <div className="cardbody">{children}</div>;
};
export const CardHeader = ({ children, isInCard }) => {
if (!isInCard) {
console.error("CardHeader was not used within a Card component");
return <div className="error">Invalid CardHeader</div>;
}
return <div className="cardheader">{children}</div>;
};
const CardBodyContainerComponent = ({ children }) => (
<div className="cardbodycontainer">{children}</div>
);
/**
* A HOC that checks if a Card Component contains an ```isInCard``` property
*
* @function withCardCheck
* @param Component - a React component
* @param type - a string of the Card component type that was used
* @returns {ReactElement}
* @example withCardCheck(Component, "example")
*/
const withCardCheck = (Component, type) => {
const wrappedComponent = (props) => {
if (!props.isInCard) {
console.error(`${type} was not used within a Card component`);
// throw Error(`${type} was not used within a Card component`);
return <div className="error">Invalid {type}</div>;
}
return <Component {...props} />;
};
return wrappedComponent;
};
export const CardBodyContainer = withCardCheck(CardBodyContainerComponent, "CardBodyContainer"
);
/**
* Checks if the child is a function (React functional component) and
* conditionally assigns it an ```isInCard``` property
*
* @function childContainsCardElements
* @param child - a React child component
* @returns {Object} either empty object or { isInCard: true }
* @example childContainsCardElements(element)
*/
const childContainsCardElements = (child) =>
typeof child.type === "function" &&
[
CardHeader.displayName,
CardBody.displayName,
CardBodyContainer.displayName
].includes(child.type.displayName)
? { isInCard: true }
: {};
/**
* Recursively iterates through children
*
* @function recursiveMap
* @returns {ReactElement}
* @example recursiveMap(children)
*/
const recursiveMap = (children) =>
React.Children.map(children, (child) => {
// checks if the child is a valid React element
if (!React.isValidElement(child)) return child;
// checks if child have their own children
if (child.props.children) {
// if so, then conditionally assign it an "isInCard" prop
child = React.cloneElement(child, {
...childContainsCardElements(child),
children: recursiveMap(child.props.children)
});
}
return child;
});
export const Card = ({ children }) => <div>{recursiveMap(children)}</div>;
import { Card, CardHeader, CardBody, CardBodyContainer } from "./Example";
import "./styles.css";
const App = () => (
<div className="App">
<h2 className="success">Valid</h2>
<Card>Card</Card>
<div className="divider" />
<h2 className="success">Valid</h2>
<Card>
Card
<CardBody>CardBody</CardBody>
</Card>
<div className="divider" />
<h2 className="success">Valid</h2>
<Card>
Card
<CardBodyContainer>
CardBodyContainer
<div>
<CardHeader>CardHeader</CardHeader>
<div>
<CardBody>CardBody</CardBody>
</div>
</div>
</CardBodyContainer>
</Card>
<div className="divider" />
<h2 className="error">Error</h2>
<CardBodyContainer>CardBodyContainer</CardBodyContainer>
<div className="divider" />
<h2 className="error">Error</h2>
<CardBody>Body</CardBody>
<div className="divider" />
<h2 className="error">Error</h2>
<CardHeader>Header</CardHeader>
<div className="divider" />
</div>
);
export default App;
从“/Example”导入{Card,CardHeader,CardBody,CardBodyContainer};
导入“/styles.css”;
常量应用=()=>(
有效的
卡片
有效的
卡片
名片夹
有效的
卡片
卡式集装箱
万向节头
名片夹
错误
卡式集装箱
错误
身体
错误
标题
);
导出默认应用程序;
其他想法
使用索引
作为键时要小心。如果子项发生任何更改,那么这可能会导致一些UI问题
另外,我建议不要抛出警告,而是抛出一个错误来摆脱递归。这样,更明显的是,某种结构是预期/强制的,而不是可选的。如果出于任何原因,您不想退出递归,并且希望减少If(!isInCard)
样板文件,那么您可以使用likewith cardcheck
简短的解释
简言之,你在孩子们的内心深处只有一个层次。您必须递归遍历子对象的子对象以指定isInCard
属性
长篇大论
React将其子对象组合为一棵树:
(2) ["Card", Object]
0: "Card"
1: Object
type: ƒ CardBodyContainer() {}
key: null
ref: null
props: Object
children: Array(2)
0: "Body Container"
1: Object
type: "div"
key: null
ref: null
props: Object
children: Array(2)
0: Object
1: Object
_owner: FiberNode
_store: Object
_owner: FiberNode
_store: Object
其中children