React: How to call function of new added child component? - javascript

I have a class component rendering multiple child components:
// inside my parent component
const allElements = arrayWithElements.map(elem => (
<ChildElement title={elem.title} />
));
// my child component
const ChildElement = ({title}) => {
// log the title to the console
const doSomething = () => {
console.log(title);
}
return (
<button type="button" onClick={doSomething}>{title}</button>
);
}
My problem is that when I add a new element to the array "allElements", I want to execute my function "doSomething" one time just for the newly added element. I thought that there might be a solution by using refs, but afaik refs are not allowed in combination with function components, so can I accomplish that?

Access your children's methods is anti pattern. To achieve the described effect you could configure each ChildComponent to log only once on mount
const ChildElement = ({title}) => {
//logging once in mount
useEffect(() => console.log(title), [])
const doSomething = () => {
console.log(title);
}
return (
<button type="button" onClick={doSomething}>{title}</button>
);
}

Related

Moving logic to child component from a parent

I am in situation where a function located in parent component handles onOK for Modal Component, the reason i have located it in parent is due to another local function that gets called once onOk button gets clicked
I would like to move it to child since Modal should be responsible for onOK logic , i can make two components hold visible state or create a stores but the question is how to go about it if another function is involved and that function is glued in parent
Parent
handleModalOk = () => {
this.onRadioButtonChange(2)
this.setState({visible: false,});
};
Child
<Modal
title={t('preferenceConfirmTitle')}
visible={this.props.visible}
onOk={this.props.onOk}
Thank you
You can pass the function located on the parent to the child. Here's an example:
// Parent.js
import React, {useState} from "react";
import Child from "./Child";
const Parent = () => {
const [myState, setMyState] = useState();
const funcOnlyInParent = () => {
console.log("I'm only in the parent");
}
const doSomething = (argsFromChild) => {
setMyState(argsFromChild);
funcOnlyInParent();
}
return (
<div>
<Child handleDoSomething={doStomething} />
</div>
)
}
// Child.js
import React, {useState} from "react";
const Child = (props) => {
return (
<div>
<button onClick={() => props.handleDoSomething("my arguments")}>Click</button>
</div>
)
}
The Child component will trigger the doSomething function in the parent and pass in any arguments that it needs to. Because the function is defined in the Parent component, then it can also perform actions on data that only the parent has access to - like setting the parent's state.
const Parent = () => {
const function1 = () => {
// your local code
}
return (
<Modal parentFunction={function1} />
)
}
const Modal = ({parentFunction}) => {
const onOK = () => {
// your local code
parentFunction()
}
return (
<button onClick={onOK}>Click</button>
)
}

simulate child component element click in parent component test file

I couldn't find a way to simulate the click of an element exists in child component from parent test file.
let card;
const displayCardSection = (cardName) => {
card = cardName;
};
describe('Parent component', () => {
it('simulate click event on child link', () => {
const component = mount(<Parent
/>);
const res = component.find(<Child
onLinkClick={displayCardSection(CM_CARD)}
/>);
expect(res).toBeDefined();
res.exists('#cm_link').simulate('click');
expect(card).toBe(CM_CARD);
})
})
This is the test file of a parent component.
Parent.jsx:
class Parent extends Component {
handleClick = () => {
console.log('link clicked!');
};
render() {
const linkText = 'Link'
return (
<Child
linkText={linkText}
onLinkClick={handleClick}
/>
);
}
}
Child.jsx:
function Child({linkText, onLinkClick}) {
return (
<Link id="cm_link" onClick={onLinkClick}>{linkText}</Link>
);
}
These are the components that I have.
I expect to simulate the link click of a child component from parent and the output should be the card displayed is 'CM_CARD'. I can write an assert statement but I'm not to simulate a click event of an element which exists in child but event handled in parent itself.
First I would suggest using shallow and the code will be the following:
const component = shallow(<Parent />);
// The following code should trigger the handler and then you will see the console output
component.find("Child").prop("onLinkClick")();
I hope this is useful and will solve your problem, Have a good day!

React 16: Call children's function from parent when using hooks and functional component

I need to call children's function in their parent component. How should I do it? Previous in React 15, I can use refs to call children's function. But have no idea how to do it with hooks and functional component.
function Child(props) {
function validate() {
// to validate this component
}
return (
<>Some code here</>
)
}
function Parent(props) {
const refs = useRef([]);
const someData = [1,2,3,4,5];
function validateChildren() {
refs.current.forEach(child => {
child.current.validate(); // throw error!! can not get validate function
});
}
return (
<>
<button onClick={validateChildren}>Validate</button>
{
someData.map((data, index) => {
return <Child ref={ins => refs.current[index] = ins} />
})
}
</>
)
}
My actual requirement is:
1. There are multiple tabs which can be added, delete base on user's behaviour
2. Click a button in the parent of tabs to trigger validate
3. All the tabs need to validate itself, show error message in their tabs and return a validate message
You can use useImperativeHandle with forwardRef.
From the docs,
useImperativeHandle customizes the instance value that is exposed to parent components when using ref. As always, imperative code using refs should be avoided in most cases. useImperativeHandle should be used with forwardRef
const Child = React.forwardRef((props,ref) => {
//validate will be available to parent component using ref
useImperativeHandle(ref, () => ({
validate() {
// to validate this component
console.log("I'm clicked");
}
}));
return (
<>Some code here</>
)
})
In the parent compoennt you can access child function as,
function validateChildren() {
refs.current.forEach(child => {
child.validate(); // you can access child method here
});
}
Demo

How to get reference to child dom element

I'm trying to implement wrapper tags that binds to events on child DOM element and then does something according to them.
I have following code:
const Wrapper: React.FC = (props) => {
// How do I get access to child div DOM element here??
someComponent.eventListenerOn(childDOMElement);
return <>{props.children}</>;
};
const ChildStuff: React.FC = () => {
return (
<Wrapper>
<div id="xyzzy"></div>
</Wrapper>
);
};
In Wrapper, how do I get access to child DOM element?
You can do that with React ref, try to check here How do I access refs of a child component in the parent component
And React document about ref: Refs and the DOM
You can use ref to access any DOM element. However, you'll need to wait until react writes its changes to DOM and then you can access it using useEffect hook. Here's how you do it using hooks:
const Wrapper = (props) => {
const ref = React.useRef(null);
React.useEffect(() => {
if (ref.current) {
// ref.current has the DOM element. Do what you want.
}
})
return <div ref={ref}>{props.children}</div>;
};
const ChildStuff = () => {
return (
<Wrapper>
<div id="xyzzy"></div>
</Wrapper>
);
};

Test if a react component's child is in fact the provided child with jest

I have a react "wrapper" component that is supposed to wrap its child. Here is the relevant part:
export class Wrapper extends Component {
render(){
return (<div>{ this.props.children }</div>);
}
}
I am trying to use jest to test if the rendered child is indeed what has been provided to this wrapper.
Here is what I tried;
describe('SwapWrapper', () => {
it('contains its child', () => {
const potentialChild = (<AMockedComponent/>);
const wrapper = TestUtils.renderIntoDocument(
<Wrapper>{potentialChild}</Wrapper>
);
const realChild = TestUtils.findRenderedComponentWithType(wrapper, AMockedComponent);
expect(realChild).toBe(potentialChild); // Obviously does not work.
});
});
It obviously does not work. realChild is a component instance while potentialChild is a component element.
Currently, the only things I have been able to do is to create potentialChild with a property and to check that realChild does contain this property.
Is there a more valid way to check if realChild corresponds in fact to the potentialChild that has been provided?
I found a solution using ref.
The ref property of a react element will be called back with the created instance when it is instantiated.
describe('SwapWrapper', () => {
it('contains its child', () => {
const ref = jest.fn();
const potentialChild = (<AMockedComponent ref={ref}/>);
const wrapper = TestUtils.renderIntoDocument(
<Wrapper>{potentialChild}</Wrapper>
);
const realChild = TestUtils.findRenderedComponentWithType(wrapper, AMockedComponent);
expect(ref).toBeCalledWith(realChild);
});
});

Categories