Shallow render a functional stateless component -react - reactjs

I have a functional component in React defined as follows
const MyComponent = ({
prop1,
prop2,
prop3
}, param1 , param2) => {
return [
//list of spans
]
}
In the spec file, I used shallow to render the component
const fakeObj = {
prop1:value,
prop2:value,
prop3:value
}
shallow(<MyComponent {...fakeObj} param1={something} param2={something} />)
However, when I console.log the params in the MyComponent, I get {} and undefined for param1 and param2 respectively,while the fakeObj is received fine.Is there any way where we can shallow render the component whilst passing object as one of the parameters?
When I just call the component from the spec file as a function ie
MyComponent({fakObj},param1,param2)
,I get the correct values for params,but not able to find the span elements properly using enzyme.

React functional component, maybe the function (function to create component) accepts only one parameter to get its props. So you can't define functional component in the way (define multiple parameters) what you did. You have to define it like following.
let MyComponent = (props) => {
// props is an object, it contains all props passed by component call.
// So you can get property value as props.propName
return 'something' // what you like to return.
}
If you use the component like bellow
<MyComponent prop1="Propone 1 Value" prop2="Prop 2 Value" prop3="Prop 3 Value" />
And console props inside your component like
let MyComponent = (props) => {
console.log(props);
return 'something' // what you like to return.
}
You will get all your passed properties as function argument (as props parameter) as an object like below.
{
prop1:"Propone 1 Value",
prop2:"Propone 2 Value",
prop3:"Propone 3 Value"
}
Now come to your requirement. You can create your component like following
let Home = (props) => {
let {fakeObj, param1, param2} = props;
return [
//list of spans
]
}
And call component like this
const fakeObj = {
prop1:value,
prop2:value,
prop3:value
}
shallow(<MyComponent fakeObj = {fakeObj} param1={something} param2={something} />)

Related

Call function in components that are rendered by a factory component

I have the following problem:
I have a react functional component A (the parent component)
In the parent Component A, a factory component named < Component /> creates different Components such as Component B,C,D by using plain JSON objects.
What I want to achieve:
Component B,C and D shall all implement a handlerFunction with specific code on their own. So the handlerFunction is not provided by the parent component, it is implemented by the Components B,C and D on their own.
I want to call the specific handlerFunction of each Component B,C, and D.
How is this possible ?
Right, functional components, on their own, cannot be assigned a react ref, but you can forward the ref or pass a ref as a named prop.
In the class-based component example you have something like
class ComponentA extends Component {
handlerFunction = () => {
console.log("A handler function");
};
render() {
return ...;
}
}
and to invoke the handlerFunction, attach the ref and call ref.current.handlerFunction() in your code
const someFunction = () => {
...
refA.current.handlerFunction();
...
}
...
<ComponentA ref={refA} />
For a functional component you can forward the ref and use the useImperativeHandle hook to "connect" the ref to the internal handler function
const ComponentB = forwardRef((props, ref) => {
useImperativeHandle(ref, () => ({
handlerFunction
}));
const handlerFunction = () => {
console.log("B handler function");
};
return ...;
});
and to invoke the handlerFunction, same thing, call ref.current.handlerFunction()
const someFunction = () => {
...
refB.current.handlerFunction();
...
}
...
<ComponentB ref={refB} />

How to initialize the react functional component state from props

I'm using React hooks for app state, I wondered about how to initialize the functional component state using props? The useState hook doc says something definitive like,
const [count, setCount] = useState(0);
I want to initialize that 0 value by the value of props being passed to the component. The Older as,
import React from 'react';
export default class Sym extends React.Component{
constructor(props){
super(props);
this.state = {
sym : [0,3,2,8,5,4,1,6],
active: this.props.activeSym
}
this.setActive = this.setActive.bind(this);
}
setActive(itemIndex){
this.setState({
active: itemIndex
});
}
render(){
return (
<div><h1>{ this.state.sym[this.state.active]}</h1></div>
);
}
}
works fine. Where the parent Component passes activeSym prop and Sym component initializes the state with it using this.props.activeSym in constructor. Is there any workaround to achieve same in function component?
First you can define it from props (if the prop exist):
const [count, setCount] = useState(activeSym);
And then you can update this value, when prop doesn't have a value immediately (when component rendered):
useEffect(() => {
if (activeSym) {
setCount(activeSym);
}
}, [activeSym])
Yes, this can be possible with functional component too! You just need to add useEffect to listen to prop change for initializing state with prop value
export const newComponent = (props) => {
const { path, value, info, update } = props;
const [val, setVal] = useState(value);
useEffect(() => {
setVal(value);
}, [value]);
return <div>{val}</div>;
};
Attching sandbox link
https://codesandbox.io/s/confident-agnesi-ohkq7?file=/src/MakeComponent.js
Yes you can first define state using props:
const [name, setName] = useState(props.obj?.name);
And then you can if the state is still undefined means props doesn't have a value, then:
useEffect(() => {
if (JSON.stringify(props.obj) !== "{}") {
setName(props.obj?.name);
}
}, [props.obj])
Just as follows :
const MyFunctionalComponent = ({myProp}) => {
const [count, setCount] = useState(myProp)
return (
/* ... */
)
}
There are two ways you can change the state:
one is using this.state and
another one is this.setState.
We use the first method to initialize the state in the constructor, and the second method is used for the rest of the time.
Initialize State in the Constructor
One way is to initialize the state is in the constructor. As we discussed earlier constructor is the first method to be called when React instantiates the class. This is the perfect place to initialize the state for the component because the constructor is called before the React renders the component in the UI.
class WithConstructor {
constructor() {
this.state = {
name: "StackOverflow"
}
}
}
Initialize State Without Constructor
Another way of initializing state in React is to use the Class property. Once the class is instantiated in the memory all the properties of the class are created so that we can read these properties in the render function.
class WithoutConstructor {
state = {
name: "StackOverflow"
}
}

How do I call a method within a Child component from the Parent in React Native

How do I call a method within a Child component from the Parent in React Native? What I essentially want to do is emulate what componentDidMount() does for class components in a functional component.
I've been getting the error "Function components cannot be given refs" and that I may want to use React.ForwardRef().
ps. idk how i would go about reformatting the child observer, pardon the bad formatting
class Dashboard extends Component {
constructor(props) {
super(props);
this.load = React.createRef();
componentDidMount() {
this.load.current.loadAudio();
}
render(){
latestEP.query = ref => ref.orderBy("id", "desc").limit(1);
return(
{latestEP.docs.map(doc => (
<DashboardItem key={doc.id} doc={doc} ref={this.load} />
))}
)
}
}
const DashboardItem = observer(({ doc }) => {
function loadAudio(){
return console.log("works")}
return (// stuff that requires loadAudio to run first)
})
You can achieve that by using useImperativeHandle hook. Please check this out:
https://reactjs.org/docs/hooks-reference.html#useimperativehandle
Wrap DashItem in forwardRef and implement useImperativeHandle hook like below:
const DashItem = React.forwardRef(({doc}, ref) => {
useImperativeHandle(ref, () => ({
loadAudio: () => {
return console.log("works");
}
}));
...
The error "Function components cannot be given refs" should be self-explanatory: you need to change DashItem to be a class component instead of a functional component.

React useRef Hook with Flow Typings

I'm using React useRef with Flow typings and I'm trying to write a wrapper component for a third party web components library.
The web component expects a changeCallback function and I'm using the ref to assign it to the ref.
function RadioButtonGroup({ onChange, children }) {
const ref: { current: null | ElementRef<ElementType> = React.useRef(null);
React.useEffect(() => {
if (ref.current) ref.current.changeCallback = onChange;
}, [onChange]);
return <web-component ref={ref}>{children}</web-component>
}
Since HTMLElement does not contain a property called changeCallback flow throws an error.
Cannot assign handleChange to ref.current.changeCallback because property changeCallback is missing in HTMLElement
I tried extending "ElementType" with the property like this
ElementRef<ElementType & { changeCallback: Function }>
But this results in the following error:
Cannot instantiate ElementRef because object type [1] is not a React component.
The web component does not fire the "change" event on change. It executes the function changeCallback. Here's the documentation for the library.
// MyComponent.js
class MyComponent extends Component {
constructor() {
// ...
// Create a ref
this.sdxSelectEl = React.createRef();
}
componentDidMount() {
// Attach callback here to ref
this.sdxSelectEl.selectCallback = (selection) => console.log(selection);
}
render() {
// ...
<sdx-select ref={el => (this.sdxSelectEl = el)} />
// ...
}
}
The solution is to call useRef with an explicit type argument to represent the expected type:
const ref = React.useRef<null | (HTMLElement & { changeCallback: Function })>(null);
I believe you need to use addEventListener to attach the callback to a web component:
if (ref.current) ref.current.addEventListener('change', onChange);

Passing props to dynamic react component

So I have the below code to load a custom component called foo. Loading of the component works fine, but props arent passing to it like I would prefer
Container component
....
const id= foo
React.createElement(LoadComponent(id, attributes))
...
Custom component
export const LoadComponent = (id, attributes) => {
/*This will load up foo.js*/
const Component = require(`./${id}`);
return Component;
};
How do I pass attributes prop to the Component in this case? I keep getting render exceptions.
Here is a simplified demo: https://codesandbox.io/s/zrw7x1zrrp
props are being passed to the component as the second parameter of createElement
const id = "foo";
const LoadComponent = props => {
return props.id;
};
ReactDOM.render(
React.createElement(LoadComponent, { id }, null),
document.getElementById("root")
);
React.createElement(component, props, ...children)
https://reactjs.org/docs/react-without-jsx.html
It's a bit confusing what you are trying to do. If you are using JSX you shouldn't need to invoke React.createElement.
If you insist of doing it without JSX though, React.createElement can take 3 parameters as per React's API. So, in your case your code will be React.createElement(LoadComponent, { id, attributes }, null) where the third parameter is the children.
Now, the id and attributes are accessible from within the props object of your custom component. So you have two options:
Destructure the props object:
export const LoadComponent = ({ id, attributes }) => {
/*This will load up foo.js*/
const Component = require(`./${id}`);
return Component;
};
Use the props object directly:
export const LoadComponent = (props) => {
/*This will load up foo.js*/
const Component = require(`./${props.id}`);
return Component;
};

Resources