For the purposes of documenting our library, I want to convert a React Component function to JSX. For example, if we have a rendered Button component, I want to show the code for how it's constructed.
One solution could be to read Button.jsx as plain text but I feel like there should be a better solution.
// Button.jsx
export const Button = (props) => (
<button {...props}><Icon name={props.icon}/>{props.children}</button>
)
// ButtonDocs.jsx
import { Button } from 'components/Button';
const Docs = (props) => {
const renderedButton = <Button icon='home'>Hello</Button>
// Here I'd expect this function to return something like:
// `<button><Icon name="home"/>Hello</button>`
const buttonJSX = someFunctionToGetJSX(renderedButton)
return (
<div>
{renderedButton}
<code>
{buttonJSX}
</code>
</div>
)
}
Do this in Button component
const Button = (props) => {
const buttonRef = useRef();
const [jsxEl, setJsxEl] = useState("");
useEffect(() => {
let jsxArray = [];
for (let i = 0; i < buttonRef.current.children.length; i++) {
jsxArray.push(`${buttonRef.current.children[i].innerHTML}`);
}
jsxArray.join(",");
setJsxEl(jsxArray);
props.onJsxFunc(jsxEl);
}, [buttonRef]);
return (
<Fragment>
<div ref={buttonRef}>
<button {...props}>
<Icon name={props.icon} />
{props.children}
</button>
</div>
</Fragment>
);
};
export default Button;
Then in ButtonDocs component do the below.
// ButtonDocs.jsx
import { Button } from 'components/Button';
const Docs = (props) => {
const renderedButton = <Button onJsxFunc={(el) => showJsxElements(el)} icon='home'>Hello</Button>
const jsxCode = useRef();
const showJsxElements = (el) => {
jsxCode.current.innerText += `${el}`;
};
return (
<div>
{renderedButton}
<code>
<div ref={jsxCode}></div>
</code>
</div>
)
}
Related
I have 2 buttons, Single Component and Multiple Component.
When I click on Multiple Component, I expect it to add 3 components, but it adds only 1.
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
import { observer } from "mobx-react-lite";
function App() {
const [component, setComponent] = useState([]);
useEffect(() => {});
const newArray = [1, 2, 3];
const Test = observer(() => {
return (
<div>
<p>Test</p>
</div>
);
});
const Test2 = observer(() => {
return (
<div>
<p>Test2</p>
</div>
);
});
const Test3 = observer(() => {
return (
<div>
<p>Test3</p>
</div>
);
});
async function MultipleComponent() {
newArray.map(async (x) => {
if (x === 1) {
await setComponent([...component, Test]);
} else if (x === 2) {
await setComponent([...component, Test2]);
} else {
await setComponent([...component, Test3]);
}
console.log(x);
});
}
return (
<div>
{component.map((Input, index) => (
<Input components={component} key={index} />
))}
<button onClick={() => setComponent([...component, Test])}>
Single Component
</button>
<button onClick={() => MultipleComponent()}>Multiple Component</button>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
codensadbox: https://codesandbox.io/s/react-hooks-useeffect-forked-edmgb5
There is no point in using await on setState, nowhere the docs say it is a good idea.
On the other hand you need to use version of setState which accepts an updater function, there you can get previous state.
setComponent(ps=>[...ps, Test2])
Also, I don't have link to official docs, but I am not sure storing components inside state is good idea either. You could store some identifier in state which indicates which component it is and then render that one when time comes. Here is what I mean by this:
let Test1 = (props) => {
return <div>1</div>;
};
let Test2 = (props) => {
return <div>2</div>;
};
let GeneralComponent = (props) => {
if (props.comp === '1') return <Test1 />;
if (props.comp === '2') return <Test2 />;
return null;
};
export default function App() {
let [comp, setComp] = React.useState('1');
return (
<div onClick={() => setComp(comp === '1' ? '2' : '1')}>
<GeneralComponent comp={comp} />
</div>
);
}
The GeneralComp accepts an identifier of which component to render, which is stored in state in parent.
counterScreen.js
import React, { useState } from "react";
const CounterScreen = () => {
const [count, setCount] = useState(0);
return (
<div>
<h2>This the number: {count}</h2>
</div>
) }
export default CounterScreen
addButton.js
import React from 'react'
const AddButton = () => {
return (
<div>
<button>+</button>
</div>
) }
export default AddButton
subtractButton.js
import React from 'react'
const SubtractButton = () => {
return (
<div>
<button>-</button>
</div>
) }
export default SubtractButton
i want when i click the button in addbutton.js the counter should add 1 and when i click the button in subtractbutton.js the counter should subtract 1
what will be the best way to share the state here please help
One simple way to solve this is to put the state in the containing component, and pass in values or callbacks to the relevant components:
const Counter = () => {
const [count, setCount] = useState(0);
return (
<div>
<CounterScreen count={count}/>
<AddButton onClick={() => setCount(count+1)}/>
<SubtractButton onClick={() => setCount(count-1)}/>
</div>
);
};
const CounterScreen = ({count}) => {
return (
<div>
<h2>This the number: {count}</h2>
</div>
)
};
const AddButton = ({onClick}) => {
return (
<div>
<button onClick={onClick}>+</button>
</div>
)
};
const SubtractButton = ({onClick}) => {
return (
<div>
<button onClick={onClick}>-</button>
</div>
)
};
I am making a simple website in React that has two like buttons.
Each like button is in different Card components and both of these are in a parent Projects component.
I want the total number of likes on the website not to exceed and 10. so button one registered 4 likes the second one cannot register more than 6 likes.
This is my initial code and it just gives the same state for both Card components
import styles from "./Card.module.css";
const Card = ({ img, title, description, ytLink, children}) => {
return (
<div className={styles.card}>
<img className={styles["card-media"]} alt={img.alt} {...img} />
<div className={styles["card-details"]}>
<h2 className={styles["card-head"]}>{title}</h2>
<p>{description}</p>
<div className={styles.buttons}>
<a
key={ytLink}
className={styles["card-action-button"]}
href={ytLink}
target="_blank"
>
Watch
</a>
{children}
</div>
</div>
</div>
);
};
export default Card;
import Card from "../Card";
import styles from "./Projects.module.css";
import React, { useState, useEffect } from "react";
import Likebutton from "../Like";
const Projects = ({ projects }) => {
const [likesCount, setLikesCount] = useState(0);
const addLike =() => {
const updateLikes = likesCount + 1;
updateLikes < 10 ? setLikesCount(updateLikes) : setLikesCount("10+");
}
const like=<Likebutton addLike={addLike} likesCount={likesCount}></Likebutton>
return (
<div className={styles.container}>
{projects.map((project) => (
<Card
key={project.title}
{...project}
>{like}</Card>
))}
</div>
);
};
export default Projects;
const Likes = () => {
const [likesA, setLikesA] = useState(0);
const [likesB, setLikesB] = useState(0);
const [message, setMessage] = useState();
const addLike =(like) => {
if(likesA + likesB < 10){
if(like == "A")
{
setLikesA(likesA +1)
}
if(like == "B")
{
setLikesB(likesB +1)
}
}
else{
setMessage("Limit was exceeded")
}
}
return (
<>
<div onClick = {() => addLike("A")}>
Add Like A
</div>
<div onClick = {() => addLike("B")}>
Add Like B
</div>
{message && message}
</>
);
};
I want to copy the div content on button click.
import React from 'react';
const App = () => {
const copyCode = () => {
//TODO
}
const data = "www.test.com";
const srcCode = `<script src=${data}></script>`;
return (
<>
<div>
{srcCode}
</div>
<button onClick={copyCode}>
Copy
</button>
</>
);
}
export default App;
This could be simple using react-copy-to-clipboard library
const data = "www.test.com";
const srcCode = `<script src=${data}></script>`;
return (
<>
<div>{srcCode}</div>
<br />
<CopyToClipboard text={srcCode} onCopy={() => alert("copied")}>
<button>Copy</button>
</CopyToClipboard>
</>
);
Codesandbox for demo
I have the following setup in react, now it complains that setClose is not a function inside the add to cart. I am stuck as to how I would trigger the setclose useState from inside the add to cart componenet, I guess I can't pass it as a prop down to the child. Not sure what to do at this point.
Thanks ahead of time
main component
const [close, setClose] = useState(true)
const toggleCart = () => {
setClose(!close)
}
return (
<AddToCart
cartAdd={setClose}
/>
{close ? <CartItems /> : null}
)
add to cart componenet
import React from "react"
import { useShoppingCart, formatCurrencyString } from "use-shopping-cart"
const AddToCart = ({ sku, setClose }) => {
const { addItem } = useShoppingCart()
const test = () => {
setClose(false)
}
return (
<div>
<button onClick={(() => addItem(sku), test())}>ADD TO CART</button>
</div>
)
}
export default AddToCart
const [close, setClose] = useState(true)
const toggleCart = () => {
setClose(!close)
}
return (
<AddToCart
cartAdd={toggleCart}
/>
{close ? <CartItems /> : null}
)
const AddToCart = ({ sku, cartAdd }) => {
const { addItem } = useShoppingCart()
const handleButtonClick = () => {
addItem(sku);
cartAdd();
}
return (
<div>
<button onClick={handleButtonClick}>ADD TO CART</button>
</div>
)
}
use like this
UPDATED