I'm getting back into React and want to stick close to best practices. With that said, I couldn't find any documentation for the best way to pass functions into a child component. More specifically, If the function I'm passing to the child component needs to use props from the parent component as arguments, which of the following is more in line with best practices.
<Button
increaseCount={() => props.increaseCount(props.title)}
label="loadMore"
/>
const Button = props => {
return <button onClick={props.increaseCount}>{props.label}</button>;
};
or
<Button
title={props.title}
increaseCount={props.increaseCount}
label="loadMore"
/>
const Button = props => {
return (
<button onClick={() => props.increaseCount(props.title)}>
{props.label}
</button>
);
};
Both will work, but since this is a small application, I may not see any performance impacts or maintainability issues that could arise as the app grows.
It depends on your Button logic, the "Rule of thumb" is passing the minimum properties which are necessary:
// JSX
<Button
increaseCount={() => props.increaseCount(props.title)}
label="loadMore"
/>
// `title` is not used within the component
const Button = props => {
return <button onClick={props.increaseCount}>{props.label}</button>;
};
On the other hand, misuse of unnecessary properties opens opportunities for bugs.
Also, there is a use case when you may break your existing component logic.
For example, when your app grows, you may want to change increaseCount logic to accept two parameters and not one, you then will need to remember visiting the Button implementation and change it accordingly:
// You may misuse the `props.title`
// and component may break on changing `increaseCount` logic
<Button
title={props.title}
increaseCount={props.increaseCount}
label="loadMore"
/>
// v props.title not used within the component
const Button = props => {
return (
// v Notice the bug, `props.label`
// v What happens when increaseCount logic changes?
<button onClick={() => props.increaseCount(props.label)}>
{props.label}
</button>
);
};
Related
I need to implement a reusable modal. I have basically two implementations in my mind.
Handle the modal visibility in the parent state. Then I'll have to pass the state setter into the modal component as I'll always have a button inside the modal to close the modal.
const Modal = (props) => {
return (
<div>
<button onClick={() => props.setVisibility(false)}>Cancel</button>
</div>
);
};
export default function App() {
const [showModal, setShowModal] = useState(false);
return (
<div className="App">
<button onClick={() => setShowModal((p) => !p)}>Toggle Modal</button>
{showModal && <Modal setShowModal={setShowModal} />}
</div>
);
}
Handle the modal visibility in the modal component itself, but expose a function to show/hide the modal using an imperativeHandle.
const Modal = React.forwardRef((props, ref) => {
const [showModal, setShowModal] = useState(false);
useImperativeHandle(ref, () => ({
hide: () => setShowModal(false),
show: () => setShowModal(true),
toggle: () => setShowModal((p) => !p)
}));
if (!showModal) return null;
return (
<div>
<button onClick={() => setShowModal(false)}>Cancel</button>
</div>
);
});
export default function App() {
const modal = useRef();
return (
<div className="App">
<button onClick={() => modal.current.toggle()}>Toggle Modal</button>
<Modal ref={modal} />
</div>
);
}
I prefer the second one, as I think it looks cleaner, without a bunch of states polluting the parent component(there will be multiple modals on one page). The toggle function is only there for the example, I'll only ever need the show and hide functions. Plus, the visibility of the modal is a property of the modal, and I feel like the modal should be the component that is handling it. But I've heard people and even the react docs saying
imperative code using refs should be avoided in most cases
So, even though I think this is an acceptable use of refs, that's what's holding me back. I want to know,
which of the two is the react way of doing this
are there any differences in performance between the two ways
are there possibilities of bugs occurring if I decide to use refs
Regarding you 3 questions:
The react way is definitively the way without useImperativeHandle. React code should be declarative, not imperative.
I don't know about performance. I would not expect a huge difference.
The useImperativeHandle will technically work fine, there will be no immediate bugs. But the idea behind the react way is to keep the code maintainable, easy to understand, and avoid future bugs and confusion.
Examples for problems with the imperative way:
Imagine you suddenly need to open / close a modal from multiple components. If the state is in the parent,
this change is simple, you can easily pass the state and the setState callback where ever you want.
If you use useImperativeHandle you need to make sure the state is consistent, which in essence means
handling the state in some parent component anyway.
Or imagine you decide to (or need to) refactor to use a reducer pattern, and dispatch a openModal action instead of
calling the show "method" of your component. Again, that is easy if you are only handling states and callbacks anyway.
How to follow the declarative way, and why:
I totally understand your desire to keep the state inside the modal. I feel the same (and would be happy to be corrected here).
But I'm also happy time and again that I didn't couple some state with other code, when I need to make "a quick change".
If you have multiple states and don't want to clutter your main component, you can use a custom hook, as you probably know.
And that's the beauty of this declarative style:
You don't have to think too much about that custom hook for now,
you can just move the states in there as they are.
If you later feel that you should rename something or organize the state differently
(but still following the react way), it is easy to throw states around between components
(or hooks, or redux, ...),
always following the same patterns,
thinking only about what you are doing right now, moving states, without thinking of the logic they are related to,
without getting confused too much.
I'm having some difficulty passing data that I have mapped from an API, it displays a bunch of Event cards based on how many events there are in the API array.
This my Event Cards component;
export default function EventCard() {
const classes = useStyles();
const [data, setData] = useState([]);
useEffect(() => {
const fetchData = async () => {
const result = await axios("http://localhost:23455/Event");
setData(result.data);
};
fetchData();
}, []);
const handleClick = (value) => () => {
console.log(value);
};
return (
<div>
<Row>
{" "}
{data.map((item) => (
<Card
className={classes.root}
style={{ marginRight: "25px", marginBottom: "25px" }}
>
<CardHeader
avatar={
<Avatar aria-label="recipe" className={classes.avatar}>
{item.icon}
</Avatar>
}
action={
<IconButton aria-label="settings">
<MoreVertIcon />
</IconButton>
}
title={item.name}
subheader="September 14, 2016"
/>
<CardMedia
className={classes.media}
image={LordsLogo}
title="Paella dish"
/>
<CardContent>
<Typography variant="body2" color="textSecondary" component="p">
<p key={item.id}> {item.description}</p>
<p key={item.id}> {item.startDate}</p>
<p key={item.id}> {item.endDate}</p>
</Typography>
</CardContent>
<CardActions disableSpacing>
<IconButton aria-label="add to favorites">
<Button variant="outlined">Steward</Button>
</IconButton>
<IconButton aria-label="share">
<Button variant="outlined" onClick={handleClick({ item })}>
Tickets
</Button>
</IconButton>
</CardActions>
</Card>
))}
</Row>
</div>
);
}
I have an onclick function which logs what data is being added into "value" onclick, if I click a card, the console log the value of that specific card:
What I'm trying to do now is use this information in another component called ServiceForm. I want to be able to click the button, be linked to ServiceForm, and use the variables within "Item" in the ServiceForm component, Ideally as shown below;
<Form.Group as={Col} controlId="formGridForeName">
<Form.Label>**{item.description}**</Form.Label>
<Form.Control
name="firstName"
placeholder="Enter name"
onChange={this.handleChange}
/>
</Form.Group>
EventCard if a functionalComponent and ServiceForm is a class based component, how could I pass the information from the first to the latter? Thanks
Edit: To show component hierarchy
ServiceForm is rendered in ServiceApplication, as shown:
import * as React from "react";
import { ServiceForm } from "../components/ServiceForm";
class ServiceApplication extends React.Component {
render() {
return (
<React.Fragment>
<h1>Service Application</h1>
<h6>Users can use this page to apply for tickets.</h6>
<ServiceForm />
</React.Fragment>
);
}
}
export default ServiceApplication;
EventCard component is rendered in EventsPage, as shown below;
import EventCard from "../components/EventCards/EventCard";
class EventsPage extends React.Component {
render() {
return (
<React.Fragment>
<h1>Events</h1>
<h6>
Welcome to the Events, here you can apply for seats or tickets at the
events shown below.
</h6>
<Row>
<EventCard />
</Row>
</React.Fragment>
);
}
}
export default EventsPage;
The idea is to pass on the ID when Clicking the 'Tickets' button on the EventCard (the ID is being pulled from an API and mapped).
It's a question of how you want to store state in your app really.
Take a simple example where a component which is a child of another. In that case, the parent can store the state locally e.g. useState() and pass it down as props to the child.
const SomeParent = () => {
const [isTrue, setIsTrue] = React.useState(true)
return (
<Child isTrue={isTrue} />
)
}
There may be cases where you want to share state across multiple components, or across your whole app. In that case, you have a load of different options.
1. Lifting state up to the highest point you need.
It would be easiest to move your state which is shared between multiple components up to the highest point that the two components share.
e.g.
Section component <- store state here
Parent one
child one
child two
Parent two
child one
Here you can again either useState() or alternatively store the state in a useReducer() and pass dispatch down.
The data flow would then look something like this:
initialise state in the section component e.g. const [someState, setSomeState]
pass the setSomeState callback down to the Parent component and in turn child component as a prop e.g.
In the child component set state onClick using the callback e.g. onClick = {() => action(item)}
pass the state value down to your second Parent component and in turn child component e.g.
You have access to the state in your form
This can be an object or multiple state values. As complexity increases, I tend to reach for useReducer().
2. Implementing useContext OR Redux in your application to create an application state accessible from any component (This is a much bigger explanation and there are lots of resources on each...
NOTE: I'm not sure why you're using two sets of brackets here:
const handleClick = (value) => () => {
console.log(value);
};
and when you're calling handleClick you need to switch it for an arrow function if you're passing a value:
onClick={handleClick({ item })
...
onClick={() => handleClick(item)}
Try this:
const handleClick = (value) => {
console.log(value);
};
<Button variant="outlined" onClick={() => handleClick({ item })} />
basically, you want to trigger a class component function from a functional component by passing item prop, if it was a parent child components you can pass a function as a prop and trigger it whenever you want from your parent or child component.
but in your case both components are not connected, what you can do without using redux is to create a public local storage for ex let's call it localStorage.js :
var PublicData = (function() {
var data = null;
var getData = function() {
return data;
};
var setData = function(props) {
data = props;
};
var clearData = function() {
data = null;
};
return {
GET: getData,
SET: setData,
CLEAR: clearData
}
})();
export default PublicData;
then you import it and use like the following :
import PublicData from './PublicData.js';
PublicData.SET(apiData); // you set your data once you fetch it
const data = PublicData.GET(); // get data
PublicData.CLEAR(); // clear your data
Note : this is not an actual localStorage, but it work the same, it will help you to share variables between your components.
Parent
/ \
Service Event
I'm guessing from your question that a simplification of your app tree looks like the above.
The problem you are having is the following:
Parent
/ \
Service Event
Data
In a top down architecture Service does not know about Event and any data over there in the that branch.
So you have 2 options (likely more, but these 2 get you a long way):
Hoist the data
Parent
Data
/ \
Service Event
Store the data somewhere else and provide a mechanism for access
Data
Parent
/ \
Service Event
Option 1 can be achieved by passing props (the data) and functions (passed using props) to manipulate the data (or, indeed, populate it in the first place i.e. fetch it)
The flow would look something like:
Parent is stateful. Parent passes the current state of the data to children that care about it (in your case that looks like Service and Event). Parent passes a function to Event, which can be attached to a click handler within the Event subtree of elements.
Due to passing a function down, when that click handler is invoked it can set the state of the Parent, from which the normal top-down rendering flow will handle setting updates and passing the data changes down the tree, whereby they will (usually) invoke a re-render.
I think Recoil uses this method, and it is becoming increasing popular once more as we look to split applications up and eschew global state management.
Option 2 is where data management libraries like Redux, Cerebral, MobX et al live. If you’re familiar with the publisher/subscriber pattern then you'll know how they work.
In essence, these libraries hold shared state for your application, such as the data you have here. They then (typically) provide a pattern for you to manage changes to the state (via a published event) and ensure that components that subscribe to that data receive the new state and update.
The 'discussion' here differs from option 1:
The click handler publishes an event, which, in essence, asks for some manipulation to the data. Manipulation occurs (or not), and subscribers are updated. In your case, the click would tell the centralised store/s (i.e. anything listening) to fetch the data, when it is done and the data is changed (from empty to filled) it lets all concerned elements (or, quite possibly, the entire application tree and leverage top-down rendering) know about it and they can do what they need to do.
Which option you pick is entirely up to you, they both have pros and cons.
Good luck thinking about your data, how you store it, and how you use it!
I'm glad you asked this question because this is a problem that has been faced and solved a lot of the times. Other contributors have written nice comprehensive answers so I'll keep it short.
1. Move shared stated to a common ancestor.
This one's pretty straight forward but gets very hairy very quickly.
2. Use Context API
You can use react's context API to move code responsible for fetching data to your context and use that data anywhere in your app without a lot of hassle. This approach is getting quite popular nowadays. Take a look at this and here's a sweet library to help you get started.
3. Use a state management library (preferred)
You can choose to use any state management library you like to solve this problem. There are quite a few options, and redux is the most popular one and for a very good reason. It enables you in implementing separation for concern between your data and your UI, but is a bit verbose.
Mobx is also popular and has a good developer experience but I personally don't like that it enforces a certain style to your code.
If you're new to react and state management then these libraries could be really daunting. I'd suggest you start with zustand. It's super simple and gets the job done. ;)
You can simply use Custom Event https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent. It is not the best practice but it work.In EventCard, when user click you dispatch a Custom event with payload is item, then ServiceForm will listener that event and store item as state. Working demo here
https://codesandbox.io/s/distracted-aryabhata-ec6gc
EventCard
const handleClick = () => {
const item = {
description: "This is description"
};
const event = new CustomEvent("customEvent", {
detail: item
});
window.dispatchEvent(event);
};
Service Form
const [item, setItem] = useState(null);
...
useEffect(() => {
const handler = (e) => {
setItem(e.detail);
};
window.addEventListener("customEvent", handler);
return () => window.removeEventListener("customEvent", handler);
}, []);
So, the parent is like this:
...
render() {
<SomeChild onSomeAction={this.onSomeAction} />
<OtherChild onSomeAction={this.onSomeAction} />
}
...
And I need to pass one and the same parameter with different values from each of the child components.
So - which one is better?
1. Provide different values from each of the child components:
SomeChild:
...
<button onClick={(e) => this.props.onSomeAction(e, true)}>Button</button>
...
OtherChild:
...
<button onClick={(e) => this.props.onSomeAction(e, false)}>Button</button>
...
2. Specify the specific value in the parent:
Parent:
...
render() {
<SomeChild onSomeAction={(e) => this.onSomeAction(e, true)} />
<OtherChild onSomeAction={(e) => this.onSomeAction(e, false)} />
}
...
SomeChild:
...
<button onClick={this.props.onSomeAction}>Button</button>
...
OtherChild:
...
<button onClick={this.props.onSomeAction}>Button</button>
...
I would put this information as high as possible, in this case, in the parent. If your child component does not need to know the boolean value (or any objects) passed to them and if it is not supposed to change their behavior or affect them in any way other than this function, they should not not be aware of this variable.
I would take it a step further and use curried functions to improve readability :
onSomeAction = value => ev => {/* */}
render() {
<SomeChild onSomeAction={this.onSomeAction(true)} />
<OtherChild onSomeAction={this.onSomeAction(false)} />
}
A component should in general only contain information modifying their behavior to be as reusable and flexible as possible.
Using this solution you could later on send whatever you want from the function :
<SomeChild onSomeAction={this.onSomeAction('hi')} />
Or nothing :
onSomeAction = ev => {/* */}
<SomeChild onSomeAction={this.onSomeAction} />
And what you want to receive will only be seen by the parent anyway, and does not need to appear in the child.
Both solutions are valid. None is better than the other.
The only difference is related to onSomeAction.
If parent has the logic of that action then it's better to send to the child as a property.
Put the action inside the child only if the action is not the same.
Prefer to reuse code.
As far I understand from what you describe, action is shared between all children, so I'd say to define that inside the parent and pass to children as a property.
I wonder if it is fine to predefine some JSX and use it multiple times in different components.
const saveButton =
<div class="u-mth u-textRight">
<Button variant="flatBlue">Save</Button>
</div>;
const test = <div>{saveButton}</div>;
Is there any downside compared to a normal functional react component?
export const SaveButton = () => (
<div class="u-mth u-textRight">
<Button variant="flatBlue">Save</Button>
</div>
);
const test = <div> <SaveButton /> </div>
And what about this one instead of functional with react props:
const saveButton = (text: string) => (
<div class="u-mth u-textRight">
<Button variant="flatBlue">{text}</Button>
</div>
);
const test = <div> {saveButton(text)} </div>;
First one is simply just jsx, it's not a component.
Second one is a stateless component, which is fine as well but you have not used any props.
Third one is also just a function not a component as you have not used props. What I would do is as #estus recommended in answer.
But please also view this link which says they way you have approached is actually faster.
React component (snippet 2) will appear as in React devtools as <SaveButton>...</SaveButton>, while other options won't.
React component (snippet 2) is the way it's usually done. If a component needs to expose dynamic behaviour, parameters should be passed as props:
const SaveButton = ({ text }) => (
<div class="u-mth u-textRight">
<Button variant="flatBlue">{text}</Button>
</div>
);
and
<SaveButton text={text} />
Helper function (snippet 3) is the way how the performance of React component can be improved, by calling it directly. It can be considered preliminary optimization, unless proven otherwise.
As long as element hierarchy doesn't expose dynamic behaviour, the definition of React elements as a variable (snippet 1) has no downside under normal circumstances. If there's a possibility that dynamic behaviour can be necessary in future (custom or localized text), it will require refactoring.
It is just declaring variables. Nothing else. In this case, using ES6 and JSX.
It is the same thing as 1. just as function. This function returns what you declared under 1. Using ES6. No downsides.
The same as 2. with arguments object, actually passing parameter to function using ES6 and Type Script.
function saveButton(props) {
return <div class="u-mth u-textRight">
<Button variant="flatBlue">{props.text}</Button>
</div>;
}
const element = <saveButton text="Save" />;
ReactDOM.render(
element,
document.getElementById('root')
);
This is the way using props and pure function.
I have a simple dictionary app I'm making that returns search results. A search is queried on every onChange of the search field using a lokiJS in-memory database for the dictionary, so the results come very quick.
It is important for me to optimize the rendering of the results, so that the 50 or so filtered search results keep flowing as the user types and the queries happen on each key stroke.
Here's what I've been doing so far: (this works but is it the best/fastest way?)
Each (sync) query of the database returns an array of objects, which I then map with something like this:
queryDB(query) {
const results = queryLokiJsDB(query);
const resultsMapped = results.map((mpd) =>
<dl key={mpd["$loki"]} onClick={() => this.clickFunction(mpd.p, mpd.f)}>
<dt>{mpd.p} - {mpd.f}</dt> <dd>{mpd.e} <em>{mpd.c}</em></dd>
</dl>);
this.setState({ results: (
<div>
{resultsMapped}
</div>
)});
}
Then, once I have the results mapped like that, I add the mapped components to the state, and then the screen gets rendered with the new results.
render() {
return (
<div>
<SearchBarStuff />
<div className="results-container">
{this.state.results}
</div>
</div>
);
}
I made this when I was just learning React and I understand that people consider it really bad practice to store components in the state.
I was wondering what the best practice in terms of performance optimization would be (and code cleanness). Should I store the results array in state then render then map them into components in the render function? Would that cause any performance hits?
Another answer said that "A function in the render method will be created each render which is a slight performance hit."
And why is it so bad to store components in state? Is it just for code clarity? Keeping the state small? I appreciate your help and patience.
I'm not quite sure but I am thinking the same problem as #larz explained in comments. Also, as you mentioned the state will be clearer. So here what would I do if I were you.
First, set your state just with the results:
queryDB(query) {
const results = queryLokiJsDB(query);
this.setState({ results });
}
Then, map through the results but instead of creating your JSX immediately I would use a separate component. Pass it mpd (the element) and onClick function with its reference.
render() {
return (
<div>
<SearchBarStuff />
<div className="results-container">
{this.state.results.map( mpd => (
<Item key={mpd["$loki"]} mpd={mpd} onClick={this.clickFunction} />
) )}
</div>
</div>
);
}
Use the Item like that:
const Item = ( props ) => {
const { mpd, onClick } = props;
const handleClick = () => onClick( mpd.p, mpd.f );
return (
<dl onClick={handleClick}>
<dt>{mpd.p} - {mpd.f}</dt> <dd>{mpd.e} <em>{mpd.c}</em></dd>
</dl>
);
}
In this way, you are not using an arrow function in your onClick handler, so this function will not be recreated in every render since we use the reference.