how to append react component with appendChild method - javascript

I have to use the following component more than 1 time.
const OnePen = (props) => {
return (
<div className="cerd" key={props.ID}>
<div className=" card-body">
<h2 className="card-title">{props.title}</h2>
<p className="card-text">{props.desc}</p>
</div>
</div>
);
};
export default OnePen;
Using the above component, the following code is working fine and doing the perfect job.
import OnePen from "./OnePen";
const PensList = ({ pens }) => {
return (
<>
<div id="mainPen" className=" wrapper">
{pens.map((pen) => (
**<OnePen ID={pen.id} title={pen.title} desc={pen.description}></OnePen>**
))}
</div>
</>
);
};
export default PensList;
However, the following code does not work as it says the type of element appending with appendChild should be Node. when I create the same component with react-create-element it works fine but I don't have to do it again and again and I want to reuse the same component.
Here is the code with the problem...
commented code does the job but I don't have to create it like that.. I have to append
const handleSubmit = (e) => {
e.preventDefault();
const pen = { title, description };
axios.post("http://localhost:5000/addPen", pen).then((res) => {
if (res.status === 200) {
// const div1 = document.createElement("div");
// div1.className = "cerd";
// const key = document.createAttribute("key");
// key.value = res.data._id;
// const div2 = document.createElement("div");
// div2.className = "card-body";
// const h2 = document.createElement("h2");
// h2.className = "card-title";
// h2.innerHTML = res.data.title;
// const p = document.createElement("p");
// p.className = "card-text";
// p.innerHTML = res.data.description;
// div2.appendChild(h2);
// div2.appendChild(p);
// div1.appendChild(div2);
**document
.getElementById("mainPen")
.appendChild(
<OnePen ID={res.data._id} title={res.data.title} desc={res.data.description}></OnePen>
);**
}
});
};
return (
//some code
);
};
export default CreatePen;
Thanks for considrations

This is how you could approach this.
If you have an API, create an endpoint to fetch data from (all pens or a specific pen). Then, you could make a component where you fetch the data with axios.get() and put it in a state variable. If you put this code inside the useEffect() hook, the data will be fetched on load of the component.
// state for your pens
const [pens, setPens] = useState([]);
// fetch on load
useEffect(() => {
axios
.get("http://localhost:5000/getPens") // set to your endpoint
.then((response) => {
setPens(response.data);
})
.catch((error) => {
console.log(error);
});
}, []);
Then, you could render the pens by mapping through them like you did before to display all of them, or you could display a single pen.
/***********
* Render
***********/
const pensDisplay = pens.map((p, i) => {
return (
<div key={i}>
{/* for example */}
<p>{p.size}</p>
</div>
);
});
return (
<div>
<h3>Pens</h3>
{pensDisplay}
</div>
);
In React, you should avoid using appendChild() and such and try doing as much as possible in the "React" way.

Related

Updating State in React Component causing it to get unmounted

I have a component where-in I need to fetch some data and render it. The component gets rendered initially. The problem I'm facing is when the handler function switchDocumentType is called after clicking the button for a particular type, the whole component gets unmounted/un-rendered.
While debugging on my own I found this happens after setDocumentType is run inside event handler function.
What is wrong in the below code snippet that could possibly cause this issue? I can see the useEffect is not going in infinite-loop as well.
Code snippet:
import * as React from 'react';
const MyComponent = (props) => {
const [documentType, setDocumentType] = React.useState('alpha');
const [documentData, setDocumentData] = React.useState('');
const types = ['alpha', 'beta', 'gamma'];
React.useEffect(() => {
myDataFetch('https://example.com/foo/?bar=123').then(async (response) => {
const data = await response.json();
setDocumentData(data.terms); // html string
const myDiv = document.getElementById('spacial-div');
myDiv.innerHTML = data; // need to render raw HTML inside a div
});
}, [documentType]);
const switchDocumentType = (type) => {
setDocumentType(type);
// send some analytics events
};
const convertToPDF = () => {
// uses documentData to generate PDF
};
return (
<div className="container-div">
{types.map((type) => {
return (
<button key={type} onClick={(type) => switchDocumentType(type)}>
{type}
</button>
);
})}
<div id="special-div" />
</div>
);
};
export default MyComponent;
You shouldn't edit the DOM directly. React has two DOMs, a virtual DOM and a real DOM. Rendering can be a bit finicky if you decide to edit the real DOM.
You can parse html safely, by using html-react-parser. This is the best way to do it, because it becomes part of the react tree whereas dangerouslySetInnerHTML will replace the entire HTML to flush changes to the DOM. With reconciliation, it can create exponential load times.
It will also sanitize your inputs, you know.. for safety. :)
import parse from 'html-react-parser';
const SpecialDiv = ({html}) => {
const reactElement = parse(html);
return reactElement
}
If you decide that you must use dangerouslySetInnerHTML you can do it as so:
const [someHTML, setSomeHTML] = useState(null)
const someFunction = async() => {
const response = await getData();
const data = await response.json();
setSomeHTML(data);
}
return(
<div>
{someHTML && <div dangerouslySetInnerHTML={{__html: someHTML}} id="special-div"/>}
</div>
)
That being said, I would say that by allowing this, you open yourself up to the possibility of a XSS attack, without properly parsing and purifying your inputs.
Do not use useEffect as handler, use useEffect hooks for initializations.
Instead of using/setting innerHtml, let react do it for you.
I suppose you have myDataFetch defined somewhere and I don't see your data fetch using the type.
Anyways, try to use the modified code below.
import * as React from 'react';
const MyComponent = (props) => {
const [documentType, setDocumentType] = React.useState('alpha');
const [documentData, setDocumentData] = React.useState('');
const types = ['alpha', 'beta', 'gamma'];
const fetchData = async () => {
const response = await myDataFetch('https://example.com/foo/?bar=123')
const data = await response.json();
setDocumentData(data);
}
React.useEffect(() => {
fetchData();
}, []);
const switchDocumentType = async (e, type) => {
e.preventDefault();
setDocumentType(type);
await fetchData();
// send some analytics events
};
return (
<div className="container-div">
{types.map((type) => {
return (
<button key={type} onClick={(e) => switchDocumentType(e, type)}>
{type}
</button>
);
})}
<div id="special-div">{documentData}</div>
</div>
);
};
export default MyComponent;
Not sure why but placing debuggers before state update causes this issue, not only for this component, but for all the other components I tried with. Seems to be an issue either with debugger or React. Removing debuggers solved the issue.
Also, now I'm returning a cleanup function inside useEffect as pointed out in some stack-overflow posts. I also refactored the code as suggested by #iaq and #sheepiiHD to follow React best practices.
Updated code:
import * as React from 'react';
const MyComponent = (props) => {
const [documentType, setDocumentType] = React.useState('alpha');
const [documentData, setDocumentData] = React.useState('');
const types = ['alpha', 'beta', 'gamma'];
const fetchData = async () => {
const response = await myDataFetch('https://example.com/foo/?bar=123')
const data = await response.json();
setDocumentData(data);
}
React.useEffect(() => {
fetchData();
return () => {
setDocumentType('');
setDocumentData('');
};
}, []);
const switchDocumentType = async (e, type) => {
e.preventDefault();
setDocumentType(type);
await fetchData();
// send some analytics events
};
return (
<div className="container-div">
{types.map((type) => {
return (
<button key={type} onClick={(e) => switchDocumentType(e, type)}>
{type}
</button>
);
})}
<div id="special-div" dangerouslySetInnerHTML={{__html: documentData.terms}} />
</div>
);
};
export default MyComponent;

How can I display the results of map function in jsx?

I'm slowly learning react and trying to display the results of my searchMap function (movie title/poster) with the TMDB API. I can log the information I need to the console, but I get undefined variables and other errors when trying to display the information in the commented div.
https://codesandbox.io/s/ddmdu4
function App() {
const search = async (event) => {
const searchQuery = event.target.value;
if (searchQuery) {
const searchReq = await fetch(
`https://api.themoviedb.org/3/search/movie?api_key=${process.env.API_KEY}&query=${searchQuery}`
).then((res) => res.json());
const searchResults = searchReq.results;
searchMap(searchResults);
}
};
const searchMap = (searchResults) => {
searchResults.map((movie) => {
console.log(`${movie.title}`);
console.log(`${movie.backdrop_path}`);
});
};
return (
<div className="App">
<div>
<input type="text" onChange={search} placeholder="Search"></input>
</div>
<div>{/* Display movie title/poster*/}</div>
</div>
);
}
export default App;
Since you want to update the DOM each time the result changes I would recommend using that inside of a state like so:
const [searchResults, setSearchResults] = React.useState([]);
In your async search function update the state by using its appropiate "setter":
.then((res) => res.json());
setSearchResults(searchReq.results);
And inside your return you can map the result as follows:
<div>
{searchResults.map((movie) => (
<>
<div>{movie.title}</div>
<div>{movie.backdrop_path}</div>
</>
))}
</div>

retrieve parent component ref for a hook

I've found myself needing to retrieve the element ref for every parent component that my hook, useExample, is used in. However, I'm stumped as to how I might be able to retrieve something like this or how to even check if there is an element to target?
Usually I would just do something a little "hacky" in a functional component like so:
const Example = WrappedComponent => {
const ref = createRef();
return <WrappedComponent ref={ref} />;
};
However, due to it being a hook and returning information and not a component, I can't target any component, and thus I'm very stumped.
My current code:
const useExample = () => {
const [stateValue, setStateValue] = useState("example");
useEffect(() => {
// Run some code...
}, []);
return stateValue;
};
const Component = () => {
const data = useExample();
return (
<div> /* <--- How do I gain access to this element */
<span>{ data }</span>
</div>
);
};
I could probably pass a created ref which has been attached to the parent div as a parameter to useExample, however this feels cheap and hacky, and I feel there should be a much easier solution.
In the ideal world something like this would be amazing:
const ref = React.getParentRef();
Apologies if there is an obvious answer in the documentation, I'm very new to React and am unsure of the correct question to be asking or what to be looking for in order to find it in the docs.
You can return the ref from the hook
const useExample = () => {
const myRef = React.useRef(null)
const [stateValue, setStateValue] = useState("example");
useEffect(() => {
// Run some code...
}, []);
return [myRef , stateValue];
};
const Component = () => {
const [myRef , data] = useExample();
return (
<div ref={myRef}> /* <--- How do I gain access to this element */
<span>{ data }</span>
</div>
);
};
If data can be a component:
const useExample = () => {
const myRef = React.useRef(null);
const [stateValue, setStateValue] = React.useState("example");
React.useEffect(() => {
const parent = myRef?.current?.parentNode;
console.log(parent);
}, []);
return <div ref={myRef}>{stateValue}</div>;
};
const Component = () => {
const data = useExample();
return (
<div>
<span>{data}</span>
</div>
);
};
export default function App() {
return <Component />;
}
But then you have to access the parent node from the ref, I believe this may cause problems as a component is being returned, and its anti pattern

How to create full screen Overlay Search bar - reactjs

I have created a Search bar, here is the code please have a look
But I am looking for an overlay full-screen search bar For reference adding the image ( https://www.kindpng.com/imgv/mJxwxw_search-full-screen-overlay-css-full-screen-search/)
can anyone help me out with these?
I'm doing this in react. How can I do this?
import { Form, ListGroup, Jumbotron } from "react-bootstrap";
export default function Search() {
// SET INITIAL STATE FOR query AND jokes
// CREATE REF FOR SEARCH INPUT
const [query, setQuery] = useState("");
const [jokes, setJokes] = useState([]);
const focusSearch = useRef(null);
// useEffect - FOCUS ON SEARCH INPUT
useEffect(() => {
focusSearch.current.focus();
}, []);
// FETCH API DATA
const getJokes = async (query) => {
const results = await fetch(
`https://icanhazdadjoke.com/search?term=${query}`,
{
headers: { accept: "application/json" }
}
);
const jokesData = await results.json();
return jokesData.results;
};
// PREVENTS RERENDER FLICKERING AS USER TYPES IN SEARCH
const sleep = (ms) => {
return new Promise((resolve) => setTimeout(resolve, ms));
};
// useEffect - ONLY RERENDERS WHEN query IS CHANGED
useEffect(() => {
let currentQuery = true;
const controller = new AbortController();
const loadJokes = async () => {
if (!query) return setJokes([]);
await sleep(350);
if (currentQuery) {
const jokes = await getJokes(query, controller);
setJokes(jokes);
}
};
loadJokes();
return () => {
currentQuery = false;
controller.abort();
};
}, [query]);
// RENDER JOKES
let jokeComponents = jokes.map((joke, index) => {
return (
<ListGroup.Item key={index} action variant="secondary">
{joke.joke}
</ListGroup.Item>
);
});
// RENDER COMPONENT
return (
<>
<Jumbotron fluid>
<Form id="search-form">
<h4>Search</h4>
<Form.Control
type="email"
placeholder="Search for a Joke..."
ref={focusSearch}
onChange={(e) => setQuery(e.target.value)}
value={query}
/>
</Form>
<ListGroup>{jokeComponents}</ListGroup>
</Jumbotron>
</>
);
}
What is the approach I should take?
I'd completly scrap your code, then I would use React Portals to create a sibling div.
In the index.html file, add this where the #root div is located.
<div id="root"></div>
<div id="modal"></div>
Now we have to create a React Portal, so create modal component. Inside that:
import React from 'react';
import ReactDOM from 'react-dom';
export default function Modal() {
return ReactDOM.createPortal(
<p>JSX for modal goes here</p>,
document.querySelector('#modal')
);
}
I recently wrote a blog on dev.to where I go over react portals in detail. Check it out https://dev.to/akashshyam/an-introduction-to-react-portals-3im0.
Now, all you have to do is add your styling and functionality. On clicking the cross you can set the display to none.

shopping cart array not rendering properly, react.js

after a fetch if I click to some card I am able to populate an empty array.
I would like to pass it as a prop to a child component and I guess I am doing the right way, the problem occurs when within the children I am trying to console log it because I can not see any errors and the console.log is not printing anything
let shoppingCart = [];
const fetchProducts = async () => {
const data = await fetch(
"blablablablablab"
);
const products = await data.json();
setProducts(products);
console.log(products);
};
const handleShoppingCart = product => {
shoppingCart.push(product);
console.log(shoppingCart);
return shoppingCart;
};
Inside the return function I tried to check if the array was not empty, if was not undefined or if was not null but with the same result
{shoppingCart.length !== 0 ? (
<ShoppingCart parkingSlots={shoppingCart} />
) : null}
children component
const ShoppingCart = ({ parkingSlots }) => {
console.log(parkingSlots);
const parkingSlotsComponent = parkingSlots.map((parkingSlot, i) => {
// const { name } = parkingSlot;
return (
<div className="parking_details" key={i}>
{parkingSlot.name}
</div>
);
});
return <div className="checkout">{parkingSlotsComponent}</div>;
};
The data is in props.
When data is passed to child component via props, then it is part of props child component. Try below and see if you can console log the data.
const ShoppingCart = props => {
console.log(props.parkingSlots);
const parkingSlotsComponent = props.parkingSlots.map((parkingSlot, i) => {
// const { name } = parkingSlot;
return (
<div className="parking_details" key={i}>
{props.parkingSlot.name}
</div>
);
});
return <div className="checkout">{parkingSlotsComponent}</div>;
};

Categories

Resources