GraphQL HOC messes with ref to children | React - reactjs

I am using React and Apollo for my project.
I got this in my component MenuModal:
onClick = () => {
console.log(this.child)
this.child.onSubmit(); // do stuff
};
render() {
return (
<Modal.Content scrolling>
<MenuEdit
ref={ref => (this.child = ref)} // Using ref to access it's properties
selectedValues={selectedValues}
match={match}
menu={menu}
/>
My component MenuEdit has a function defined in class:
onSubmit = values => {
console.log('calling inner form submit', values);
if (this.child) {
this.child.submitFromOutside();
}
};
I should be able to call onSubmit from MenuModal right?
But I am currently getting this:
And when I console.log this.child in my onClick function I can see this:
So there's no onSubmit function there. When seeing GraphQL I wondered if it had something to do with me exporting the component with the graphQL HOC.
export default compose(
graphql(UPDATE_MENU, { name: 'updateMenu' }),
withApollo,
withRouter
)(MenuEdit);
And when I changed it just to:
export default MenuEdit;
I can see my function
So I wonder how I write my export so I still can access my function in my child. Thanks.

The HOC wrapps your component into another component. You can see this in the React devtools. You will see that the component renders a component around your wrapped component. Something like this
<Apollo(ChildComponent)>
<ChildComponent />
</Apollo>
Your ref then points to the Apollo(ChildComponen) element instance.
What you are doing here looks like an antipattern to me. In React we usually don't call functions on rendered elements (except sometimes DOM elements). The idea is rather that children call functions of their parents by receiving them as properties. The best thing in your case is to get rid of the reference and move your state and actions up the component chain. Alternatively you can use the new render prop style in react-apollo.

There was a contribution to the Apollo repository to address this issue...
https://github.com/apollographql/react-apollo/pull/410
Wrapping your component export like this withApollo(Component, { withRef: true }) will expose your child methods. Accessible using ref.current.wrappedInstance.

Related

How do I mock the return value of a react hook passd as a prop when testing with jest

I'm trying to write a unit test for a Card compenent using Jest. This component takes a framer-motion value and a ref as Props. This values can only be created using the useMotionValue and useRef hook.
interface Props {
currentIndex: MotionValue<number>;
containerRef: RefObject<HTMLDivElement>;
}
Currently, I'm able to pass in a dummy value for the ref using document.createElement, but when i tried creating a motion value to pass into the component, I get an error saying that react hooks cannot be used ouside a component
it("renders a card", () => {
const container = document.createElement("div");
render(
<Card
containerRef={{ current: container }}
currentIndex={******} // motion value goes here
/>
);
});
I had to render the component in a parent component first and then test the parent Component but i was wondering, is there a way to mock the motion value and pass it in as a prop?
The easiest way would be to mock useMotionValue implementation. You can use jest.mock at the top of your file where you write your test for the component that uses this hook. Remember also to not mock other functionality by requiring actual implementation.
// at the top of the file that tests component with useMotionValue call
jest.mock('framer-motion', () => ({
...jest.requireActual('framer-motion'),
useMotionValue: jest.fn().mockReturnValue({
// put here the mock implementation of MotionValue methods
})
}));

Is it possible to pass data to components without a parent?

I understand that the normal way is to pass props through a parent, but I want to know how else I can pass props to a component.
let C1 = createReactClass({
...
render: function() {
console.log('render C1');
return (
<div>
<button onClick={this.cambiaAAzul}>Azul</button>
<button onClick={this.cambiaAVerde}>Verde</button>
<button onClick={this.cambiaARojo}>Rojo</button>
<p>Estado C2 <strong style={ {color: this.state.color} }>{this.state.color}</strong></p>
<C2 color={this.state.color}/> // It is common
</div>
);
}
});
Typically, to solve the props-drilling problem, which is what seems to be the issue you are trying to solve, the useContext hook should help.
So set up a context,
const C1ContextProvider = ({children}) => {
//...
// the Provider gives access to the context to its children
return (
< C1Context.Provider value={someValue}>
{children}
</C1Context.Provider >
);
}
... and then in your component where you need someValue, you can just do this:
import React, { useContext } from "react";
import { C1Context } from "../C1Context";
//...
const values = useContext(AirDCPPSocketContext);
cons foo = values.someValue; // <-- someValue obtained from the context
//..
I don't know how it is possible to not have parents but you can pass informations by local storage and context hooks.
Props are by definition arguments passed into a React component. Since React components are instantiated in the context of their parent, I'm not sure if it would be possible to pass props in any other way.
There are many other ways to get data in React though. You could query data from an API or other source, or you could pull from a state manager (such as redux). It just depends on what you are trying to accomplish.
Props are simply things passed into a component when it's called.
For instance:
C1.js
function C1((prop1, prop2) {
<h1>{prop1} {prop2}</h1>
})
App.js
<C1 prop1="Hello" prop2="World" />
<C1 prop1="React's" prop2="Great" />
By referencing the C1 component in App.js, you can call it with different props to produce different results.
Now I'm not too familiar with the createReactClass and render: you're using, as this is a style of React I've never learned. However, I'd guess that props provide the same function.
Your question asks about props without a parent. Well. Props without a parent wouldn't be props, they'd just be normal JS data types inside your file.
Hope this helps :)
The component's local state only can drill into child components. for passing props without using React component's local state. you have to use state management libraries like Context API or Redux
These libraries are created to make you able to pass props into nonrelated or sibling components
Example with redux:
Here I Assume that you set up redux in your project. imagine you have two components that you want to pass props without parent-child relation.
const changeMyWeightAction = (weight) => {
return {
type: 'CHANGE_MY_WEIGHT',
payload: weight
}
}
const JenniferFunctionComponent = () => {
const dispatch = useDispatch()
return (
<div>
<span>hello I'm Jennifer and I can decide my weight</span>
<input onChange={e => dispatch(changeMyWeightAction(e.target.value)})/>
</div>
)
}
const WeightFunctionComponent = () => {
const weight = useSelector(state => state.Jenny.weight)
return (
<span>Jenny's weight is {weight}</span>
)
}

Composition In React with params

I am trying to create an architecture that in some way imitates the slots from VUE.
The idea is for the parent component to be able to inject some props into the component and the child can inject the rest of the props.
This is how I tried to approach this problem, unfortunately this approach will not work because the compontent will be "monut" every time the parent re-render takes place.
Filters = (prams) => {
useEffect(()=>{ //RENDER ALL THE TIME },[])
...
}
ParentComponent = () => <ChildComponent Filters={(props) => <Filters propA={"A"} />}
ChildComponent = (props) => {
const Filters = props.Filters;
render(<Filters probB="B" />)
}
I know, I can use useCallback for ((props) => <Filters propA={"A"} />), but only it will help only if what I want to pass to "propA" is steady.
I want to "manage" <Filters /> component in parent, so that the child does not have to handle Filters logic (props).
React gives you proper API to do most things. Using it forces you into certain paradigms that are proven to work well.
You should probably have a look at the Context and Memo APIs from React.
Or if you have to select and update state from multiple components, you might wanna have a look at libraries that provide global state, like Redux and Recoil.
Context example
// The shape
interface ContextProps {
myProp: string
}
// The context
export const MyContext = React.createContext<Partial<ContextProps>>({
myProp: 'nothing'
});
// The provider
<MyContext.Provider value={{ myProp: 'override' }}>
{children}
</MyContext.Provider>
// Consumer
const { myProp } = useContext(MyContext)
In some case you can also use useMemo or React.memo and use your own custom compare function if needed to prevent re-renders in very specific situations.

Why, exactly, do we need React.forwardRef?

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?

How to change state of component from anywhere without Redux?

Is this bad practices or not ?
export state change function from component
import it from other file.
call the function to change state?
In this way we can change some component state from anywhere.
For example...
We want to change the Model.js state from anywhere.
Modal.js
import React from 'react';
export let toggleModal;
export default class Modal extends React.Component {
constructor(props) {
super(props);
this.state = {
open: false,
};
toggleModal = this.toggleModal;
}
toggleModal = () => {
this.setState({ open: !this.state.open });
};
render() {
const { open } = this.state;
return <div style={{ color: 'red' }}>{open && 'Hello Modal'}</div>;
}
}
App.js(Some Top Level component)
import React from 'react';
import Modal from './Modal';
export default () => (
<>
...
<Modal />
...
</>
);
Somewhere.js
import React from 'react';
import {toggleModal} from './Modal';
export default () => (
<>
<h1>Hello!</h1>
<button onClick={() => toggleModal()}>open Modal!</button>
</>
);
  
But there is no reference in React Official docs, so is this bad practices ?
What React Docs recommends...
Just passing function props to change parent state from parent to children
Use context
Redux or Mobx
But, these are too complex for me.
Example code here
https://next.plnkr.co/edit/37nutSDTWp8GGv2r?preview
Everything seems pretty much overwhelming and difficult at the beginning. But as we get out hands on them, it's give us more confidence to dig into.
I would recommend to use redux that's how we tackled props drilling problem. You can dispatch a action and connect reducer to corresponding component which upon updating state will re render. This is what I recommend to most of the people to learn the tale of redux with a real life example:
Understanding Redux: The World’s Easiest Guide to Beginning Redux
Apart from this you can take Dan Abramov, author of the library, free redux course on egghead.io:
Getting Started with Redux
The problem you run into, almost immediately like your code example does is this:
It will not work: your toggleModal() method expects a this to refer to an actual component instance. When your onClick() handler fires you invoke toggleModal() as a plain function. The this context will be wrong, and so at best (in your example) you will get an error because you try to invoke something undefined, at worst (in general) you end up invoking the wrong method.
When you think about it, for any non-trivial React component you will have a hard time obtaining a reference to the actual instance that is currently being used: you have to make sure that you are not forgetting to invoke the method on the right component instance and also you have to consider that instances may be created/destroyed 'at will' for whatever reason. For example: what if your component is rendered indirectly as part of some other component's render() method? Multiple layers of indirection like that make it even harder.
Now, you could fix all that by abusing ref with abandon but you will find that now you have to keep track of which ref refers to what particular instance, if you happen to have multiple of the components to consider in one render tree...
Whenever you think one component needs to handle the state of its siblings, the solution is usually to lift the state one level up.
export default class Modal extends React.Component {
render() {
const { isOpen } = this.props;
return <div style={{ color: 'red' }}>{isOpen && 'Hello Modal'}</div>;
}
}
export default class Home {
this.state = {
isOpen: false,
};
toggleModal = () => {
this.setState({ isOpen: !this.state.isOpen });
}
render() {
const { isOpen } = this.state;
return (
<>
<h1>Hello {name}!</h1>
<button onClick={() => this.toggleModal()}>open Modal!</button>
<Modal isOpen={isOpen}/>
<p>Start editing and see your changes reflected here immediately!</p>
</>
)
}
}
This way the Home handle the state and your problem is solved.
This can get annoying if the state needs to be "drilled down" to children, that's a problem than redux or react-context can solve.
Here <Modal /> is the child component. So to call a function in a child component you can simply use Ref.
You can refer this page to get more info about Ref.
You can assign a class variable as a ref to this child and use this class variable as an object to call its function.
I found if in special case, my way is okay.
Special case means something like customAlert component.
It is okay only one instance of customAlert component mounted at a time in App.
To achieve this...
1.Use ref to access and change DOM
2.attach state changing function or component to window and call window.function
3.my case: export state changing function and import it from other file.
And here is how to do with react Context
https://next.plnkr.co/edit/EpLm1Bq3ASiWECoE?preview
I think Redux is overkill if the main thing you are interested in is to make some states-like data available and updatable throughout your App without props drilling.
For that purpose, a much simpler approach (maybe not available at the time the question was posted?) is to use react context: https://frontend.turing.edu/lessons/module-3/advanced-react-hooks.html
"context - an API given to us by React, allowing for the passing of
information to child components without the use of props
[...]
useContext - a react hook, allowing functional components to take
advantage of the context API"

Resources