React component name from factory - javascript

I have two components:
const CustomSelfDefinedComponent = () => <React.Fragment />;
const CustomSelfDefinedViaFactoryComponent = (() => () => <React.Fragment />)();
on shallow rendering I get next:
<CustomSelfDefinedComponent />
<Component />
Can anyone point me why in second case I don't have CustomSelfDefinedViaFactoryComponent as a component name in a snapshot?
I see the reason at the fact that it compiles as : [BABEL PLAYGROUND]
"use strict";
var CustomSelfDefinedComponent = function CustomSelfDefinedComponent() {
return /*#__PURE__*/React.createElement(React.Fragment, null);
};
var CustomSelfDefinedViaFactoryComponent = function () {
return function () {
return /*#__PURE__*/React.createElement(React.Fragment, null);
};
}();
Any solutions?

I suppose React relies on function.name to get default component name, so in first case
const CustomSelfDefinedComponent = () => <React.Fragment />;
CustomSelfDefinedComponent.name -> "CustomSelfDefinedComponent"
while in second anonymous function returned and name = ""
To solve that factory function can have explicit displayName argument:
const factory = (..., displayName) => {
const NewComponent = () => { ... }
NewComponent.displayName = displayName
return NewComponent
}
not sure if there's a babel plugin that can simplify it

Related

retrieve parent component ref for a hook

I've found myself needing to retrieve the element ref for every parent component that my hook, useExample, is used in. However, I'm stumped as to how I might be able to retrieve something like this or how to even check if there is an element to target?
Usually I would just do something a little "hacky" in a functional component like so:
const Example = WrappedComponent => {
const ref = createRef();
return <WrappedComponent ref={ref} />;
};
However, due to it being a hook and returning information and not a component, I can't target any component, and thus I'm very stumped.
My current code:
const useExample = () => {
const [stateValue, setStateValue] = useState("example");
useEffect(() => {
// Run some code...
}, []);
return stateValue;
};
const Component = () => {
const data = useExample();
return (
<div> /* <--- How do I gain access to this element */
<span>{ data }</span>
</div>
);
};
I could probably pass a created ref which has been attached to the parent div as a parameter to useExample, however this feels cheap and hacky, and I feel there should be a much easier solution.
In the ideal world something like this would be amazing:
const ref = React.getParentRef();
Apologies if there is an obvious answer in the documentation, I'm very new to React and am unsure of the correct question to be asking or what to be looking for in order to find it in the docs.
You can return the ref from the hook
const useExample = () => {
const myRef = React.useRef(null)
const [stateValue, setStateValue] = useState("example");
useEffect(() => {
// Run some code...
}, []);
return [myRef , stateValue];
};
const Component = () => {
const [myRef , data] = useExample();
return (
<div ref={myRef}> /* <--- How do I gain access to this element */
<span>{ data }</span>
</div>
);
};
If data can be a component:
const useExample = () => {
const myRef = React.useRef(null);
const [stateValue, setStateValue] = React.useState("example");
React.useEffect(() => {
const parent = myRef?.current?.parentNode;
console.log(parent);
}, []);
return <div ref={myRef}>{stateValue}</div>;
};
const Component = () => {
const data = useExample();
return (
<div>
<span>{data}</span>
</div>
);
};
export default function App() {
return <Component />;
}
But then you have to access the parent node from the ref, I believe this may cause problems as a component is being returned, and its anti pattern

How to pass string parameter to nested function, which has already parameters?

This is how my Categories react functional component looks like. For easier testing, I split up the handleClick and the react component itself - but that shouldn't be an issue for this question.
How do I pass the component string value from the map() to the handleClick()? handleClick already passes some operator parameter, which gets me struggling with this simple issue...
export const useCategories = () => {
const handleClick = (operator) => {
updateCategory({
variables: {
id: '123',
operator,
category // <-- this value is missing
}
})
}
return {
icon: {
onClick: handleClick('$pull') // <-- Here I add some operator value
}
}
}
export const Categories = () => {
const { icon } = useCategories()
return (
<div>
{categories.map((category) => <Icon onClick={icon.onClick} />)} {/* <-- how to pass category value to handleClick...? */}
</div>
)
}
In order to "add" a variable, you can use currying.
Hence, return a function that receives the new argument and use it internally:
export const useCategories = () => {
return (category) => {
const handleClick = (operator) => {
updateCategory({
variables: {
id: '123',
operator,
category // now this is the argument of the wrapper function
}
})
};
return () => handleClick('$pull');
};
}
And you can use it like this:
export const Categories = () => {
const getOnClick = useCategories();
return (
<div>
{categories.map((category) => {
const onClick = getOnClick(category);
return <Icon onClick={onClick} />;
})}
</div>
)
}

navigate function in Gatsy return undefined

Faced a problem with the navigate() in Gatsy function, using the navigate function, you can pass an object as a second parameter to another page. I did as in the documentation, but when getting data I get undefined.
const Panel = () => {
const [name, setName] = useState('')
useEffect (() => {
if (name !== undefined) {
setName('Hello World')
}
}, [name])
const handleRedirect = () => {
navigate('/cabinet/', { state: { name }})
}
return (
<div>
<button onClick={handleRedirect}>Redirect</button>
<div>
)
}
const Cabinet = ({location}) => {
console.log(location.state.name) // undefined
return (
<div>
<h1>{location.state.name}</h1>
</div>
)
}
Your problem is a matter of timings/asynchronously. Your navigate built-in function looks good, however, you are setting the name (with your setName setter) after the navigation occurs. In addition, you are missing the key for the state. Try something like:
const handleRedirect = () => {
if(name) navigate('/cabinet', {state: {'name': name}}); // Try hardcoding Hello World for debugging purposes {'name'; 'Hello World'}
}
Then in your Cabinet component:
const Cabinet = ({location}) => {
console.log(location.state.name) // will be your Hello World
return (
<div>
<h1>{location.state.name}</h1>
</div>
)
}
Alternatively, you can do an async/await approach, something like:
const setNameFunction = ()=>{
setName('Hello World')
return name
}
const handleRedirect = async () => {
let nameFromFunction= await setNameFunction;
navigate('/cabinet/', { state: { 'name': nameFromFunction }}) // use if(nameFromFunction) navigate('/cabinet/', { state: { name: nameFromFunction }}) alternatively
}
This second approach will make the ecosystem dynamic, allowing you to pass a custom name based on some logic that you will need to add (passing the name as a parameter through those functions).
Alternatively, you can look for your state in your Cabinet component using window object in window.history.state.

Mocking a higher order component with jest

I want to ensure that a HOC component is being called with jest, but I can't seem to get jest.mock to work. My HOC is like this:
const withEntity = (
...args
) => {
const wrappedComponent = WrappedComponent => {
const innerComponent = ({ ...props }) => {
return (
<WrapperComponent
{...props}
>
<WrappedComponent />
</WrapperComponent>
);
};
innerComponent.propTypes = {
...
};
return innerComponent;
};
wrappedComponent.propTypes = {
...
};
return wrappedComponent;
};
withEntity.propTypes = {
...
};
export default withEntity;
In a separate file, the withEntity function is called like this:
export const DoSomething = withEntity(...args)(MyComponent);
Then, in the testing file for the DoSomething component, i'm trying to import the withEntity function and mock it like so:
import withEntity from "../../../shared/entity/higher_order_components/withEntity";
jest.mock("../../../shared/entity/higher_order_components/withEntity");
But when I actually attempt to run the test, I get this error:
● Test suite failed to run
TypeError: (0 , _withEntity.default)(...) is not a function
Not sure what to make of that error, what am I doing wrong here?
Mocking your HOC should look like this:
jest.mock('../your/HOC', () => () =>
Component => props => <Component {...props} />
)
it can be read as :
jest.mock('../your/HOC', () => `
create a mock that returns your HOC function,
() =>
the function that returns your HOC aka withEntity(...args),
Component => props => <Component {...props} />
the HOC itself, which is a function that gets the component and return a function that get the props and return a function that returns the rendered component with its props.
In my case because I am using typescript this is what works for me.
jest.mock('components/MyComponent', () => ({
__esModule: true,
default({children}: any) {
return children(() => {});
},
}));
What worked for me is basically putting the below snippet into setupTests.js if you wish the render your component without the HOC affecting it.
jest.mock('./pathTo/yourHOC', () => Component => {
return Component
})
This works for me
.mock(
'./pathTo/yourHOC',
() => () => (Component) => {
return Component;
})

How can load data of lists using component in ReactJS?

I am trying to render some data using 'ListItem' component in ReactJS. But the component is not getting the data. I also tried to load the data without using 'ListItem' component. In that case it is successfully rendering.
So, How can i load the data using 'ListItem' component?
import React, { Component } from 'react';
function ListItem(props) {
console.log(props);
return <li>{props.value}</li>;
}
class NumberList extends Component {
constructor(props) {
super(props);
}
render() {
const numbers = this.props.numbers;
const listItems = numbers.map( (number) => {
<ListItem key={number.toString()} value={number} />
});
console.log(listItems);
return(
<ul>
{
// numbers.map( n => {
// return <li>{n}</li>
// } )
listItems
}
</ul>
);
}
}
export default NumberList;
You are returning undefined in your map-function.
Try changing to this:
const listItems = numbers.map( (number) => {
return <ListItem key={number.toString()} value={number} />
});
or:
// By omitting the {} around the callback function body
// you can utilise the implicit-returning feature of arrow functions.
const listItems = numbers.map( (number) => (
<ListItem key={number.toString()} value={number} />
));
// The above example is the same as:
const listItems = numbers.map( (number) => <ListItem key={number.toString()} value={number} />);
You are using the arrow function notation inside the map. When you use it with braces ({ and }), you should add a return to the statement you wish to return.
const add = (a, b) => {
return a + b;
};
When your function has just one statement, you can omit the braces and place only the statement at the right side of the arrow, like this:
const add = (a, b) => a + b;
But if you add the braces, but does not write a return statement, then the function will return undefined. So, this would return undefined:
const add = (a, b) => {
const sum = a + b;
};
You have to return your listitem component from map. You are returning li items in your commented code. But looks like you missed adding return when converting it to use ListItem.

Categories

Resources