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.
Related
I can't believe I couldn't find an answer on this so please point me in the right direction if I am wrong.
I am trying to do something simple:
There's a function component from a library that I include.
I need to get a ref to it.
Crucially, React says this:
Ref forwarding lets components opt into exposing any child component’s ref as their own.
Unfortunately, this library component has not opted in using forwardRef.
What is the correct approach to get a ref to this element?
The 3rd party library component in question looks like so (simplified):
function OutboundLink(props) {
return (
<a {...props}
...
/>
)
}
My component looks like so:
function MyComp(props) {
const ref = useRef(null)
return (
<OutboundLink ref={ref} {...props} >
{children}
</OutboundLink>
)
}
but I am greeted with the errors about not being able to pass refs to a function component. Normally I would wrap in forwardRef, but can't in this case since I can't modify the library's code directly.
I did see this and this but didn't think either applied to me.
Any ideas?
You can't directly. There's no hook to let you in if the component wasn't written that way.
I think your best bet would be to wrap the component, capture a ref, and then drill into its children with DOM methods.
const wrapperRef = useRef(null)
const innerRef = useRef(null)
useEffect(() => {
if (wrapperRef.current) {
innerRef.current = wrapperRef.current.querySelector('a')
// innerRef.current is now the first <a> tag inside <OutboundLink>
}
})
return <div>
<div ref={wrapperRef}>
<OutboundLink {...props}>
{children}
</OutboundLink>
</div>
</div>
Codepen example (view the console)
But really, this is a bit of a hack. You should use a component that handles this better. Look into how hard it would be to write this component yourself from scratch. It may be trivial, and worth it.
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>
);
};
https://reactjs.org/docs/hooks-faq.html#how-to-test-components-that-use-hooks
the document show that we should use React DOM to test component.But in many cases, our logical components render another ui component.And maybe the function in logical component will be passed to the ui component as a prop, just like
function Foo(){
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `You clicked ${count} times`;
});
function onChange(value) {
setCount(value);
}
return <Bar value={count} onChange={onChange} />
}
function Bar(props){
const { value, onChange } = props;
return (
<div>
<p>You clicked {value} times</p>
<button onClick={() => onChange(count + 1)}>
Click me
</button>
</div>
);
}
In this case, how can I test the onChange() and other functions which can't be obtained in DOM?This is the simplest case. If we use some component library like material-design and ant-design, we usually don't know the DOM structure.
As the docs suggest:
If your testing solution doesn’t rely on React internals, testing
components with Hooks shouldn’t be different from how you normally
test components.
The goal is not to test the onChange function at all, whether the hooks work or not is already tested by the React team.
Your current change to render a component does not change the test at all, rendering Foo still renders a button and p however deep the component chain goes.
On using a framework like Antd or Material Design, it might be difficult to know the complete DOM structure. But instead, or better you can query by things that the user sees on your page.
For eg. using the React testing library recommended in the docs:
const button = getByText(container, /Click Me/i);
This ties in directly with what user is seeing on the page and leads to much better tests.
React v0.14 supports pure functional components (i.e. same input equals same output). Props are passed in as function arguments.
// Using ES6 arrow functions and an implicit return:
const PureComponent = ({url}) => (
<div>
<a href={url}>{url}</a>
</div>
);
// Used like this:
<PureComponent url="http://www.google.ca" />
// Renders this:
http://www.google.ca
But how do you render children of the PureComponent? In regular stateful components you access the children with this.props.children, but this obviously doesn't work here.
const PureComponent = ({url}) => (
<div>
<a href={url}>{children}</a> // <--- This doesn't work
</div>
);
<PureComponent url="http://www/google.ca">Google Canada</PureComponent>
// I want to render this:
Google Canada
What should I do?
You need to add children to the destructuring assignment of the argument "props".
const PureComponent = ({url, children}) => (...)
children is just a prop passed to the component. In order to use it like you are using props.url you need to add it to that list so it can be "pulled out" of the props object.
Pretty new to React, and I have this issue where 2 of my components are fairly similar, but different enough to not be the same.
I looked up a way to compose the components and avoid duplications, and with Mixins out of the way, I encountered HOC, which I think can be pretty powerful, yet confusing for somebody like me who's not yet familiar with React and its inner workings.
So I have my main component(wrappee) which will then be wrapped. In turn that component renders a child component (in my case a textarea for one of the wrappee component, the other one being an input field).
One of the reasons why I'd like to use HOC is that I bind event listeners to the text input, and so need to be able to access it from the HOC.
Textarea ---- InputController ---- InputHOC
Input ---/
In practice I have managed a way to communicate the DOM element back to the HOC, using a callback in ref. But I feel it's pretty dirty, as I have to have 2 callbacks:
InputController:
return (
<div>
{textareaHint}
<div className='longtext-input-wrapper'>
{textareaComponent}
</div>
</div>
)
textareaComponent
return <TextareaAutosize
ref = {
(c) => {
this._input = c
}
}
className={classList}
minRows={MIN_ROWS}
/>
and then in InputHOC:
<div className={submitButtonClass}>
<Button clickHandler={_buttonClickHandler.bind(this)} ref='submitButton'>
<span className='button-text'>Ok</span>
<span className='button-icon'>✔</span>
</Button>
<InputComponent ref={
(item) => {
if (item && !this.input) {
this.input = item._input
}
}
}
/>
</div>
And then I can access this.input in componentDidMount:
const inputNode = ReactDOM.findDOMNode(this.input)
It does really feel hacky, so I was wondering if there was a better way to deal with this?
Thanks a lot for your input
#Jordan is right, this is not using the HOC pattern. A good example of this pattern can be found here: https://gist.github.com/sebmarkbage/ef0bf1f338a7182b6775. You can think of a HOC as a superclass for the component that it wraps, but it's not actually a class. A HOC is a function that takes a react component and returns a new one with the desired enhancements.
What you are doing is using ref callbacks to link together all of your components and making other components aware of things other than their direct children, which is definitely not recommended. In your case, I don't see why you shouldn't place everything from InputHOC into InputController. Then, define the functions that you want to bind to the input in TextareaAutosize in InputController and pass them as props. It would look something like this:
InputController:
class InputController extends React.Component {
handleChange() {
// Note that 'this' will not be set to the InputController element
// unless you bind it manually
doSomething();
}
render() {
return (
<Button clickHandler={_buttonClickHandler.bind(this)} ref='submitButton'>
<span className='button-text'>Ok</span>
<span className='button-icon'>✔</span>
</Button>
<div>
{textareaHint}
<div className='longtext-input-wrapper'>
<TextareaAutosize callback={this.handleChange} />
</div>
</div>
);
}
}
TextareaAutosize
class TextAreaAutosize extends React.Component {
render() {
return (
<textarea onChange={this.props.onChange}>
Some text value
</textarea>
)
}
}