Which way is better for sending functions references to react components? - javascript

I'm trying to send function reference to imported component. So it's better I use ref attribute or props ?
I want to find an optimum and standard method.
<MyComponent ref="ListView" />
Or this method:
<MyComponent show="this.showModal" hide="this.hideModal" />

There wouldn't be an effect if you pass functions as a string (like "this.showModal").
Pass them down in curly braces:
<MyComponent show={this.showModal} hide={this.hideModal} />
Refs purpose is to manipulate DOM directly, not to pass something to the component. For example, you may want to manipulate DOM with jQuery

We don't use refs for event handling it is preferable to pass handlers as props to child component using JSX sytax with {} braces.

You want to send eventhandler to child component from parent if i understand your question correctly?
parent.js
class ParentComponent extends Component {
handler = () => {
console.log("handler click">
}
render() {
return (
<div>
<Child handler={this.handler} />
</div>
);
}
}
child.js
const Child = ({ handler }) => (
<Button onClick={handler} />
)

EDIT:
I understood your question, if you have a lot of method you can use ...props but also you can pass props as separated methods.
I think you're referring to the differences between controlled and uncontrolled components :
uncontrolled-
https://reactjs.org/docs/uncontrolled-components.html
controlled-
https://reactjs.org/docs/forms.html
The answer for that is depending on your component "logic", if you want to controll it by the DOM(using refs) or by the component itself.

Related

Why do we even need forwardRef in React? [duplicate]

Let's say I have a component with a scrollable subcomponent, and I want to expose the ability to scroll:
const MyComponent = (props) => {
return <ScrollView ... />
}
I want to be able to do
<MyComponent ref={myRef} />
...
myRef.scrollTo({x: 0});
So I need a way to forward the ref to the <ScrollView>. Let's try putting the ref on the props:
const MyComponent = (props) => {
return <ScrollView ref={props.scrollRef} ... />
}
...
<MyComponent scrollRef={myRef} />
...
myRef.scrollTo({x: 0});
I just tried that with React Native on iOS, and it indeed works. I see several advantages over React.forwardRef:
Simpler, because I don't need to use another React API.
Works also if there is more than one child who needs ref forwarding.
Seems to me that this approach is
What's the advantage of React.forwardRef? Why was it added in React 16.3?
Note that there is no difference between using another named prop like innerRef FOR FORWARDING, it works the same.
Refactoring class components
Since React moved toward function components (hooks) you might want to refactor the class component code to a function component without breaking the API.
// Refactor class component API to function component using forwardRef
<Component ref={myRef} />
React.forwardRef will be your only option (further explained in details).
Clean API
As a library author you may want a predictable API for ref forwarding.
For example, if you implemented a Component and someone wants to attach a ref to it, he has two options depending on your API:
<Component innerRef={myRef} />
The developer needs to be aware there is a custom prop for forwarding
To which element the innerRef attached? We can't know, should be mentioned in the API or we console.log(myRef.current)
<Component ref={myRef} />
Default behavior similar to ref prop used on HTML elements, commonly attached to the inner wrapper component.
Notice that React.forwardRef can be used for function component and HOC (for class component see alternative below).
Ref forwarding is not limited to DOM components. You can forward refs to class component instances, too.
For function components, forwardRef sometimes comes with useImperativeHandle combo (in class component you just call the class methods on ref instance: ref.current.myAttr().
// Same usage
<Component ref={myRef} />
const Component = React.forwardRef((props, ref) => {
// you can forward ref <div ref={ref} />
// you can add custom attributes to ref instance with `useImperativeHandle`
// like having ref.myAttribute() in addition to ones attached to other component.
});
Important behavior of ref prop without forwardRef.
For the class component, this code alone will attach the ref to CLASS INSTANCE which is not useful by itself and need another ref for forwarding:
// usage, passing a ref instance myRef to class Component
<Component ref={myRef} />
Full example, check the logs:
// We want to forward ref to inner div
class ClassComponent extends React.Component {
innerRef = React.createRef();
render() {
// Notice that you can't just `this.props.ref.current = node`
// You don't have `ref` prop, it always `undefined`.
return <div ref={this.innerRef}>Hello</div>;
}
}
const Component = () => {
const ref = React.useRef();
useEffect(() => {
// The ref attached to class instance
console.log(ref.current);
// Access inner div through another ref
console.log(ref.current.innerRef);
}, []);
return <ClassComponent ref={ref} />;
};
In function components, it won't even work because functions don't have instances.
By default, you may not use the ref attribute on function components because they don’t have instances. [1]
forwardRef.
Refs and the DOM.
Why we need ref forwarding?

React changing text of child on button hover

I want to change the text of a child function component when I hover over a button of my parent class component. I'm having trouble accessing the prop though in the child component and getting null. Any help is appreciated
parent component:
export default class PathfindingVisualizer extends React.Component {
constructor(props) {
super(props)
this.state = {
AlgoDef: null,
};
}
render() {
return (
<React.Fragment>
<div className="button-bar"> //buttons that change state
<button
onClick={() => this.helperDikjstras()}
onMouseOver={() => this.setState({ AlgoDef: "Dikj"})}
>Dikjstras</button>
<button
onClick={() => this.helperAstar()}
onMouseOver={() => this.setState({ AlgoDef: "Astar"})}
>A*</button>
</div>
<div>
<AlgoExplaination algoName={this.AlgoDef} /> //changes its text based on state of parent
</div>
</React.Fragment>
);
}
}
my child component:
export default function AlgoExplaination(props) {
const [text, setText] = useState("default");
useEffect(() => {
switch (props) {
case "Dikj":
setText("Dikjstra");
break;
case "Astar":
setText("Astar");
break;
default:
setText("useEff");
}
//console.log(`text: ${text}`);
//console.log(props);
// console.log(props.algoName);
});
return (
<div>
<p>{text}</p>
</div>
)
}
both console logging props gives me: {algoName: null}. and props.algoName gives me null
As #Rajesh has mentioned in a comment, you are passing props to your AlgoExplaination (sic) component incorrectly like this:
<AlgoExplaination algoName={this.AlgoDef} />
You intended to pass the AlgoDef property of your state, which is this.state.AlgoDef, so change accordingly to this:
<AlgoExplaination algoName={this.state.AlgoDef} />
Furthermore, when you access the algoName property of your props, you currently attempt to access it as if it were the props object itself incorrectly like this:
switch (props) {
The props object for AlgoExplaination will be an object with an algoName property that looks (partially) like this:
{ algoName: "Dikj" }
So, the value you need is actually stored in props.algoName. Therefore, please change accordingly to this:
switch (props.algoName) {
As #Wyck has addressed most of the points, I'll focus this answer on the last point:
Third, why do you have both class component and functional component with hooks? Please use 1 way
Class vs Functional component
In theory, a class component has lifecycle events and state, where as a functional component is just a function that returns JSX.Element. Such components were called stateless.
Because of this, class component had a bit of overhead over functional component, and as a performant option functional component were preferred.
Hooks
Hooks are a way in react to give functional components access to have its own state and few major lifecycle events. This is achieved using closure (not going in full depth) and craftsmanship. This also makes class components obsolete as everything can be achieved in functional component.
Which one to use?
As a preference, its suggested to use functional component with hooks as they are easy to use and are performant as well, in comparison.
Why to use one?
In general programming practice, as a developer, you should use a single way to do things. Benefit of this is, it helps in fast reading.
If I use a for loop in one section and a Array.forEach in next, as a developer, I will be asked to read and understand the purpose. This adds overhead and reduces readability. Having same approach moves the focus of reader to just the logic.
I recomend you to destructuring the props, to be more clear.
In
export default function AlgoExplaination(props)
Can be something like:
export default function AlgoExplaination({algoName})
So you can use it in your switch statment.
switch (algoName) {
case "Dikj":
setText("Dikjstra");
Right now, you are passing all props but you will have to access as props.algoName in the switch statment.

ReactJS, passing prop from parent to child component?

I have a bunch of components that require the same prop, and going through them individually to add the JSX is cumbersome, can I create a piece of JSX in the parent that will be passed to these child components as a prop?
Here's basically what I want to do:
function App(props) {
/*not part of the actual code, but I want to
pass this h1 tag into the bottom components as a prop*/
<h1>{props.heading}</h1>
<Home heading="Home"/>
<AboutMe heading="About Me"/>
<Contact heading="Contact"/>
}
I know the code above isn't how you're supposed to do it, but is there a way I can accomplish that functionality?
Yes, that's possible. Just assign your JSX component to a variable and pass that variable as a prop. You have the right idea.
For example:
var customHeader = <h1>Here's a custom component</h1>
You can also set customHeader to a React component such as: var customHeader = <CustomHeader />
You can pass through <MyComponent header={customHeader}/> and in your render function of MyComponent page, you can simply use {this.props.header} to load your component. This can be repeated for any number of components.
Edit based on comments. In that case, I would wrap the component in a function. For example:
getCustomHeader = (title) => {
return <MyHeader
headerTitle={title}
/>
}
Now, in your render function, you can call getCustomHeader.
render() {
return <div>
<MyComponent1 header={this.getCustomHeader("Title A")} />
<MyComponent2 header={this.getCustomHeader("Title B")} />
</div>
}

Lifing state up: Communicating from child component and upward

When I try to modify a base component's variable from a child component. I find that I can only do it by strictly doing the following:
1: Base component must have defined an event handler, strictly a variable onVariableChange event handler, and have it assigned to a local function
2: Base component must have custom attribute variable that will be linked with the above onVariableChange function
3: Child component can now call the this.props.onVariableChange() to make the appropriate modification (from child to base)
in Base declaration:
changeFn(){ //do Something }
Base's render:
return <div> <Child variable={this.stateSomeVar} onVariableChange={this.changeFn} />
in Child:
this.props.onVarChange();
Why is that? Why can't we just call the custom function from child to base directly without the use of custom property?
Am I incorrectly understanding the React's documentation?
in Base:
childFnAnsweredByBase(){
...
}
render(){
return <Child callFromChildFn={this.childFnAnsweredByBase} />
}
REF:
https://reactjs.org/docs/lifting-state-up.html
When I try to modify a base component's variable from a child
component. I find that I can only do it by strictly doing the
following:
I think you mean with variable, base or more accurate parent component's state.
1: Base component must have defined an event handler, strictly a
variable onVariableChange event handler, and have it assigned to a
local function
I don't know what do you mean by saying "strictly", but yes in order to do that parent should have a handler method. The name here is not important, just pass this properly to your child component.
2: Base component must have custom attribute variable that will be
linked with the above onVariableChange function
This variable or state property doesn't need to be linked anywhere and you don't have to pass this to your child component. If child component will use it yes you can pass, but in order to change this in the parent component, it is not needed to be passed to the child.
this.props.onVarChange();
Why is that? Why can't we just call the custom function from child to
base directly without the use of custom property?
If you mean saying by "property" the value itself, again, you don't need to pass it to the child. But, if you mean props, then you should use like that since this function is a part of the child's props.
Here is an example of how you do it without passing the "variable":
const Child = (props) => (
<div>
<input onChange={props.callFromChildFn} />
</div>
);
class App extends React.Component {
state = {
someVar: "initial value",
}
childFnAnsweredByBase = event =>
this.setState({ someVar: event.target.value })
render() {
return (
<div>
<Child callFromChildFn={this.childFnAnsweredByBase} />
<p>someVar is: {this.state.someVar}</p>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
Am I incorrectly understanding the React's documentation?
Probably yes. Personally, I don't like that part of the documentation. They are trying to explain an edge case there. Two child components are syncing with some parent's state and show this value with an appropriate situation. Like, one of them shows this value as Fahrenheit and the other one shows it as Celcius. This is why they are passing the state variable (after some conversion) to these components.
In the example above we don't use this state variable in our child component, this is why we don't need it. Here is an example (just a simple, stupid example) showing that how can we use it and why the parent is passing it.
const Child = (props) => {
const { someNum, multiplyTheNumberBy, by } = props;
const handleMultiply = () => {
const newNum = someNum * by;
multiplyTheNumberBy( newNum );
}
return (
<div>
<button onClick={handleMultiply}>Multiply Number By {by}</button>
</div>
);
}
class App extends React.Component {
state = {
someNum: 1,
}
multiplyTheNumberBy = valueFromChild =>
this.setState({ someNum: valueFromChild })
render() {
return (
<div>
<Child
multiplyTheNumberBy={this.multiplyTheNumberBy}
someNum={this.state.someNum}
by={10}
/>
<Child
multiplyTheNumberBy={this.multiplyTheNumberBy}
someNum={this.state.someNum}
by={100}
/>
<p>someNum is: {this.state.someNum}</p>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
Update after comments
Also, why do we have to assign const localFn = props.CallFromChildFn ?
why can't we just invoke this.props.CallFromChildFn directly? Or is it
supposed to be props.CallFromChildFn?
First things first. We use this in a class component, so for a functional component, it is not necessary. We can use our props as props.something instead of this.props.something.
Now, the second question is about applying the best practice for performance reasons. For a small app this may not be a problem but for larger apps which has multiple children, components may be problematic.
When defining your functions in a JSX prop, if you use an arrow function and invoke them immediately, or bind it to this there to use it properly, this function is recreated in every render. This is why use references to this functions instead of immediately invoke them somehow or use bind.
Examples.
Think about my first example.
<input onChange={props.callFromChildFn} />
Here, I used the reference of my function and it workes. Since I don't invoke any function here it is not recreated every time when my component renders. I would use it in this way:
<input onChange={e => props.callFromChildFn( e )} />
Here, we are using a callback for onChange as an arrow function. It takes an event and passes it to our callFromChildFn function. This works, too. But, since we used an arrow function here, this function is created in every render.
Let's see my second example.
const { someNum, multiplyTheNumberBy, by } = props;
const handleMultiply = () => {
const newNum = someNum * by;
multiplyTheNumberBy( newNum );
}
return (
<div>
<button onClick={handleMultiply}>Multiply Number By {by}</button>
</div>
Again, instead of using directly my function, I define a handler function here and use its reference. With this newly created function, I can do multiplication operation and use my multiplyTheNumber function from my props and pass it the calculated value. But again, I would use something like this:
const { someNum, multiplyTheNumberBy, by } = props;
return (
<div>
<button onClick={() => multiplyTheNumberBy(someNum * by)}>Multiply Number By {by}</button>
</div>
As you can see, without creating a new function we can use an onClick callback function and use our multiplyTheNumberBy from our props and do the multiplication directly there. But, this function also recreated in every render.
Yes, with the reference method we use a little more code and for small applications maybe this is not necessary. But, I like to use in this way.

Communicating between nested child components in React.js

It won't take you long to realise that I am probably out of my depth here. I am not only new to React.js but also to ES6 and so please be gentle with your answers...
Here goes.
I am using a component to build a form input:
const Input = (props) => {
return(
<input ... />
)
}
I have a component which I use to construct HTML around any of the basic form elements that I give it.
const InputWrap = (props) => {
return(
<div class="input-wrap" ...>
{children}
</div>
)
}
Which allows me to do something like this...
const Input = (props) => {
return(
<InputWrap>
<input ... />
</InputWrap>
)
}
What I would like to do is to add a character counting component to the mix:
const InputWrap = (props) => {
return(
<div class="input-wrap" ... >
{children} // which is the HTML input
{props.maxValue && <CharCounter />}
</div>
)
}
So here is my problem...
The <CharCounter /> needs to be notified of any changes happening to the <input /> and update it's internal state.
The <input /> and the <CharCounter /> are siblings and children of the <InputWrap /> and so, from what I can gather, I need a method inside <InputWrap /> which ties an onChange of the <input /> and some method that will update the state within the <CharCount /> component.
I am at a loss as to how I go about adding a callback as the <input onChange={doSomething} /> is in the form {children} by the time it comes in contact with the <CharCount /> once inside the <InputWrap />...
Where am I going wrong here? I'm starting to think it was way back at the beginning...
There are two typical ways of communication between siblings:
You use the InputWrapper as an DataContainer
You use a Data Flow library like flux or redux (which is a lot more complex, especially for this case)
For the 1. you need, as you correctly noticed, an onChange handler for the input component, which is a function defined in the Component and which is passed to the input. If your input component is an own component and not the native component you will need to pass the onChange prop to the native input.
The function in the Component takes the input, counts the chars and sets an internal state variable with setState({ charCount: #CountValue#}). And then you can pass the state variable to the CharCount Component with
One Important thing to mention: You are using stateless components and therefore you will need to change your InputWrap to a normal react component
class InputWrap extends React.Component {
...
}
Hope this will give you the right direction.
Another excellent solution that is like redux but has a different architecture and api is https://github.com/mobxjs/mobx-react.
You can use the inject HOC to inject shared state to any react component in your application.

Categories

Resources