I'm working in a project already began that's using react class version. I plan to work with react hooks, so to don't refactor all the classes, as I write new codes, I'm trying to mix those react versions (idk if it's a good idea and I should refactor all).
I'm creating a list with pagination and search. The pagination and search are in an unique component.
To this component a need pass the search character value input by user, and here is where I'm facing problem. In other words, I need pass a value to the parent component.
Code is below:
useState hook:
const [search, setSearch] = useState('');
Filter component, that change the search value:
const Filter = () => {
return (
<Card>
<Form.Group label="Filtro">
<Grid.Row gutters="xs">
<Grid.Col>
<Form.Input
name='search'
placeholder='Filtro'
autoFocus
value={search}
onChange={e => setSearch(e.target.value)}
/>
</Grid.Col>
<Grid.Col auto>
<Button
color="success"
icon="search"
onClick={filtrar}
>
</Button>
</Grid.Col>
</Grid.Row>
</Form.Group>
</Card>
);
}
function getSearchDB() {
setSearch((search) => {
return search;
})
}
Pagination component, that receive the props:
<Pagination
baseUrl={'vehicles/toUse'}
updateState={setStateDB}
getSearch={getSearchDB}
fields={'license_plate'}
/>
Printing search value pass through Pagination component:
console.log(this.props.getSearch()) //print undefined
OBS: updateState={setStateDB} is working fine.
Things done to make this work (no success):
In getSearch={getSearchDB} directly pass search value. Result: this.props.getSearch() print undefined
Defined getSearchDB() to be like:
function getSearchDB() {
return search;
}
Result: this.props.getSearch() print undefined.
Is there a way to put it to work?
Guys, let me know if the post is confusion or the English is poorly written.
Instead of passing down a function that returns search, why not just pass down search itself as a prop?
<Pagination
search={search}
const Pagination = (props) => {
console.log(props.search);
add :
<Pagination
search={search}
/>
In component Pagination :
const Pagination = ({search}) => {
console.log(search);
return {
//...
}
}
Related
I am using react and axios for frontend, and nextjs with prisma for backend. I have in the database 4000 exercices that contain fitness exercices. I want to create a function where by each key stroke, the api will look for the relevant exercice. I finished creating it, but i have some issues:
The main problem is that the response is delayed from the first keystrokes, because the payload response is tooo large. I created a scrollable UL element to render the elements, because I want to get also the Gif images. So the elements, if the API will find those, will be rendered on the screen.
If I add to each element an on click event, to select the exercice's Id, I get an error "too many re-rendering on the screen".
How can I optimise the function, and how can I solve the error of too many re-render on the screen? Nextjs tells me that it will create an infinite loop....
The frontend looks like this:
const [open, setOpen] = useState(false);
const [keyWord, setKeyWord] = useState('');
const [array, setArray] = useState([]);
const [exerciceId, setExerciceId] = useState('');
// Add exercice
const hadnleAddExercie = async event => {
event.preventDefault();
console.log('exercice added');
}
// Look for exercices
const searchExercices = async event => {
event.preventDefault();
setKeyWord(event.target.value);
const arrayExercices = await getExercicesByKeyWords(keyWord);
setArray(arrayExercices);
console.log(arrayExercices);
}
<div className='flex mt-3 flex-col'>
<input onChange={searchExercices} required placeholder='Search by word...' className='border border-slate-400 p-1 rounded-md flex-1 max-w-sm my-2'/>
<ul className='border border-slate-400 p-1 rounded-md max-w-sm my-2 max-h-52 overflow-scroll'>
{
array.length > 1 && array.map(exercice => (
<li key={exercice.id} className='flex flex-wrap p-2 bg-slate-200 m-2 items-center rounded-md'>
<span><Image className='rounded-xl mr-2' priority width={40} height={40} src={exercice.gifUrl} alt={exercice.name}/></span>
<span>{ exercice.name }</span>
</li>
))
}
</ul>
</div>
The backend Uses prisma and I use the OR clause to look for a word in different rows:
export default async function handler(req, res) {
try {
const param = req.query.slug[0];
console.log(param);
// Get exercices where the two rows contains a single parametter
const exercices = await prisma.exercices.findMany({
where: {
OR: [
{
name: {
contains: param
}
},
{
target: {
contains: param
}
},
{
equipment: {
contains: param
}
}
]
}
});
res.status(200).send(exercices);
}
catch (error) {
console.log(error);
res.status(500).send(error);
}
}
An example can be this:
Only for finding an exercice I used 500mb...
Here are a few ways I can think of to optimize this:
Use pagination and fetch more results as user scrolls down or actually separate it by using pages. You can read more on how to implement pagination in Prisma here.
Add debounce to your search term so it doesn't actually fire on every single keystroke, you could use something like useDebounce.
Use React.memo to prevent the list from being re-rendered every time some state changes, only re-render it when the actual list changes.
I have a small question regarding passing functions between components that are not in parent/child relationship.
My structure inside App.
function App() {
return (
<div className="App">
<Header/>
<Pfl />
<Sdc/>
<Checkscan/>
</div>
);
}
Those 3 components have an on click function attached to a button i want the button from the pfl component to trigger all 3 on click functions.
When i click on the button in the pfl component i want to trigger the function running in the pfl component and the functions that are inside the Sdc,Checkscan component.
Whats the best way to do it and pass the functions from the other components so when i click the button inside the pfl component it will trigger all the methods from the other 2 components(Sdc,Checkscan)?
Or if I make a container that looks like this
export default function Apicontainer() {
return (
<React.Fragment>
<Pfl />
<Sdc />
<Checkscan />
<Button variant="contained">Start</Button>
</React.Fragment>
)
}
and in app.js i only have the Apicontainer.
How do i transfer all the functions to work in that button click Component
I just wrote some quick and dirty example code to show how you can share things between components via a parent component:
export default function Apicontainer() {
const [sharedState, setSharedState] = useState({sdc: null, checkScan: null})
function pflFunction() {
console.log('pflFunction')
// do your stuff here. I would update state with some reasonable data, and then pass
// the relevant data to the component that needs it. This is just an example.
setSharedState({sdc: 'sdcData', checkScan: 'checkScanData'})
}
return (
<React.Fragment>
<Pfl onClick={pflFunction} />
<Sdc data={sharedState.sdc}/>
<Checkscan data={sharedState.checkScan} />
<Button variant="contained">Start</Button>
</React.Fragment>
)
}
// Example of how to trigger a function inside a component (separate file):
export default function Sdc({data}){
const sdcFunction = useCallback(() => {
// implement the function you want to call here. useCallback makes sure to keep
// a stable reference to the function, so that you can rely on it in a useEffect
}, [])
useEffect(() => {
if(data){
// do something. This effect will run whenever the data or sdcFunction changes.
sdcFunction()
}
}, [data, sdcFunction])
return (
<div>your Sdc view code here</div>
)
}
For the record: If fplFunction is anything else than an onClick handler, you should make sure the function has a stable reference, using useCallback (as in the last component)
This question already has answers here:
React onClick function fires on render
(13 answers)
Closed 1 year ago.
I code a page in react, it interacts with a small API code with express by me and I created a button when I click on it it deletes a message but when I don't click the function is still executed... my code:
delete(id){
for (let i = 0; i<1; i++) {
axios.delete(`http://localhost:8080/messages/${id}`)
.then(res => {
console.log(res);
console.log(res.data);
})
}
}
render() {
return (
<div>
<ul>
{<button type="submit" onClick={this.delete(messageId)}>suprimmer</button></li>}
</ul>
</div>
)
}
}
Does this work:
<button type="submit" onClick={() => this.delete(messageId)}>suprimmer</button>
When you do it as you did in your version, you are actually calling the function.
onClick receives value as its function to trigger/call/run when user "click" on it. But in your code, you trigger/call/run function this.delete() during compile phase (compiler read Button onClick={this.delete(id)}>Delete Button</button> and called this.delete(id) immediately and use the result of it as onClick's value(delete function return nothing it means {return undefined}, so the final result is ` ).
So the result is:
Everytime the component loaded, it will call delete instantly. And onClick value now is undefined and it makes onClick won't work as expected.
For more details I'll give an example below:
const DeleteButton =({id})=> {
const delete = (id)=> {
return api.deleteItem(id)
}
return <Button onClick={delete(id)}>Delete Button</button>
}
So, when I use the component above:
<Container>
<Content value={item}/>
<DeleteButton id={item.id}/>
</Container>
it will automatically delete the item you've loaded, because while render DeleteButton it called delete(id) already.
So, how to fix it? - there are many solutions for it, but ultimately it have to give the type of value of onClick is a function:
#1 I bet noone use this, but I think it is more useful to describe my idea.
const DeleteButton =({id})=> {
const delete = (id)=> {
return function() {
api.deleteItem(id)
}
}
//delete(id) called and it returns an anonymous function that receive `id` and called it when onClick trigger
return <Button onClick={delete(id)}>Delete Button</button>
}
#2
const DeleteButton =({id})=> {
const delete = (id)=> {
return api.deleteItem(id)
}
return <Button onClick={(id)=>delete(item.id)}>Delete Button</button>
//or use bind : bind is return a function that wrapped parameter into
return <Button onClick={delete.bind(id)}>Delete Button</button>
}
I have some items in a mongodb collection, now i want to view them on a react app, i've that code, but it doesn't display nothing, but if i check value with a console.log() i get the content. How i can do?
import axios from "axios";
const viewMails = []
axios.get('http://localhost:5000/emails').then(res => {
let emailString = JSON.parse(res.request.response)
for (const [index, value] of Array(emailString).entries()) {
viewMails.push(
<div key={index}>
<h1>{value.name}</h1>
<h3>{value.email}</h3>
<p>{value.message}</p>
<p>{value.createdAt}</p>
</div>
);
}
});
export default class EmailsViewer extends Component {
render() {
return (
<div className="emails">
<h1>Sos</h1>
{viewMails}
</div>
);
}
}```
Since you are trying to do a simple component to show a list, if you're using one of the last version of React, consider using axios hook (check the package documentation to see how to add it to your project https://www.npmjs.com/package/axios-hook)
Here I show you an example of what you need to do: list demo
I used to compile and insert JSX components via
<div key={ ID } dangerouslySetInnerHTML={ { __html: HTML } } />
which wrapped my HTML into a <div>:
<div>my html from the HTML object</div>
Now react > 16.2.0 has support for Fragments and I wonder if I can use that somehow to avoid wrapping my HTML in a <div> each time I get data from the back end.
Running
<Fragment key={ ID } dangerouslySetInnerHTML={ { __html: HTML } } />
will throw a warning
Warning: Invalid prop `dangerouslySetInnerHTML` supplied to `React.Fragment`. React.Fragment can only have `key` and `children` props.
in React.Fragment
Is this supported yet at all? Is there another way to solve this?
Update
Created an issue in the react repo for it if you want to upvote it.
Short Answer
Not possible:
key is the only attribute that can be passed to Fragment. In the
future, we may add support for additional attributes, such as event
handlers.
https://reactjs.org/docs/fragments.html
You may want to chime in and suggest this as a future addition.
https://github.com/facebook/react/issues
In the Meantime
You may want to consider using an HTML parsing library like:
https://github.com/remarkablemark/html-react-parser
Check out this example to see how it will accomplish your goal:
http://remarkablemark.org/blog/2016/10/07/dangerously-set-innerhtml-alternative/
In Short
You'll be able to do this:
<>
{require('html-react-parser')(
'<em>foo</em>'
)}
</>
Update December 2020
This issue (also mentioned by OP) was closed on Oct 2, 2019. - However, stemming from the original issue, it seems a RawHTML component has entered the RFC process but has not reached production, and has no set timeline for when a working solution may be available.
That being said, I would now like to allude to a solution I currently use to get around this issue.
In my case, dangerouslySetInnerHTML was utilized to render plain HTML for a user to download; it was not ideal to have additional wrapper tags included in the output.
After reading around the web and StackOverflow, it seemed most solutions mentioned using an external library like html-react-parser.
For this use-case, html-react-parser would not suffice because it converts HTML strings to React element(s). Meaning, it would strip all HTML that wasn't standard JSX.
Solution:
The code below is the no library solution I opted to use:
//HTML that will be set using dangerouslySetInnerHTML
const html = `<div>This is a div</div>`
The wrapper div within the RawHtml component is purposely named "unwanteddiv".
//Component that will return our dangerouslySetInnerHTML
//Note that we are using "unwanteddiv" as a wrapper
const RawHtml = () => {
return (
<unwanteddiv key={[]}
dangerouslySetInnerHTML={{
__html: html,
}}
/>
);
};
For the purpose of this example, we will use renderToStaticMarkup.
const staticHtml = ReactDomServer.renderToStaticMarkup(
<RawHtml/>
);
The ParseStaticHtml function is where the magic happens, here you will see why we named the wrapper div "unwanteddiv".
//The ParseStaticHtml function will check the staticHtml
//If the staticHtml type is 'string'
//We will remove "<unwanteddiv/>" leaving us with only the desired output
const ParseStaticHtml = (html) => {
if (typeof html === 'string') {
return html.replace(/<unwanteddiv>/g, '').replace(/<\/unwanteddiv>/g, '');
} else {
return html;
}
};
Now, if we pass the staticHtml through the ParseStaticHtml function you will see the desired output without the additional wrapper div:
console.log(ParseStaticHtml(staticHtml));
Additionally, I have created a codesandbox example that shows this in action.
Notice, the console log will throw a warning: "The tag <unwanteddiv> is unrecognized in this browser..." - However, this is fine because we intentionally gave it a unique name so we can easily differentiate and target the wrapper with our replace method and essentially remove it before output.
Besides, receiving a mild scolding from a code linter is not as bad as adding more dependencies for something that should be more simply implemented.
i found a workaround
by using react's ref
import React, { FC, useEffect, useRef } from 'react'
interface RawHtmlProps {
html: string
}
const RawHtml: FC<RawHtmlProps> = ({ html }) => {
const ref = useRef<HTMLDivElement>(null)
useEffect(() => {
if (!ref.current) return
// make a js fragment element
const fragment = document.createDocumentFragment()
// move every child from our div to new fragment
while (ref.current.childNodes[0]) {
fragment.appendChild(ref.current.childNodes[0])
}
// and after all replace the div with fragment
ref.current.replaceWith(fragment)
}, [ref])
return <div ref={ref} dangerouslySetInnerHTML={{ __html: html }}></div>
}
export { RawHtml }
Here's a solution that works for <td> elements only:
type DangerousHtml = {__html:string}
function isHtml(x: any): x is DangerousHtml {
if(!x) return false;
if(typeof x !== 'object') return false;
const keys = Object.keys(x)
if(keys.length !== 1) return false;
return keys[0] === '__html'
}
const DangerousTD = forwardRef<HTMLTableCellElement,Override<React.ComponentPropsWithoutRef<'td'>,{children: ReactNode|DangerousHtml}>>(({children,...props}, ref) => {
if(isHtml(children)) {
return <td dangerouslySetInnerHTML={children} {...props} ref={ref}/>
}
return <td {...props} ref={ref}>{children}</td>
})
With a bit of work you can make this more generic, but that should give the general idea.
Usage:
<DangerousTD>{{__html: "<span>foo</span>"}}</DangerousTD>