force parent component to only accept certain child components - javascript

I'm building a component that requires its direct descendants to be at least one of two types of components (ValidChildA or ValidChildB).
Using the most upvoted comment on this answer, I came up with this solution:
const ValidChildA = () => <div></div>;
const ValidChildB = () => <div></div>;
const Parent: React.FC<ParentProps> = ({ children, ...passedProps }) => {
React.Children.forEach(children, child => {
const isValidChildA = child.type.prototype instanceof ValidChildA;
const isValidChildB = child.type.prototype instanceof ValidChildB;
console.log(isValidChildA, isValidChildB); // Outputs false every time.
if(!isValidChildA && !isValidChildB){
console.error("Parent component expects only ValidChildA or ValidChildB as direct children.");
}
});
return <div {...passedProps} />
};
export default function Page(){
return (
<Parent>
<ValidChildA />
<ValidChildB />
</Parent>
);
};
Now despite the children being valid, my error is still thrown and thus the component doesn't render correctly.
I could use the displayName property to check if a child is a valid component, but since Next uglifies the code and the displayName property in production, this would break the app.

After a bunch of digging and reading (list included below), it seems like you could use named functions and then you could check the function name to see if it matched. I'm not sure if this will satisfy your requirements, but I did just try the following:
Define your ValidChild* components with function instead of the fat-arrow syntax () => .
function ValidChildA() {
return <div>A</div>
}
function ValidChildB() {
return <div>B</div>
}
Then in your checks inside the children loop, you can look at the function name and make sure it's valid.
React.Children.forEach(children, (child) => {
const isValidChildA = child.type?.name === ValidChildA.name;
const isValidChildB = child.type?.name === ValidChildB.name;
...
With this code, this component does not log the error
<Parent>
<ValidChildA />
<ValidChildB />
</Parent>
But this one does
<Parent>
<div>a</div>
<ValidChildA />
<ValidChildB />
</Parent>
The short answer ends here but I below I added some other thoughts and research that I did to get me here.
The reason what you're trying doesn't work (I think) is because
functional components don't have a prototype - they are not classes - they are functions. So if you look at child.type.prototype, you'll find it's undefined which of course will never be an instanceof anything.
I did a bunch of experimenting and found that even if you moved to class based components, these checks don't work. Seems kind of strange but...
class ValidChildB extends React.Component {
render() {
return <div>Bprime</div>
}
}
If we render this and add some logs...
React.Children.forEach(children, child => {
console.log(`Prototype ${child.type.prototype}`);
console.log(`instanceof object? ${child.type.prototype instanceof Object}`)
console.log(`instanceof Component? ${child.type.prototype instanceof React.Component}`)
console.log(`instanceof ValidChildB? ${child.type.prototype instanceof ValidChildB}`)
...
})
we get
// for ValidChildA - functional component
"Prototype undefined"
"instanceof object? false"
"instanceof Component? false"
"instanceof ValidChildB? false"
// for ValidChildB - class component
"Prototype [object Object]"
"instanceof object? true"
"instanceof Component? true"
"instanceof ValidChildB? false"
I did a bunch of reading and a Typescript guard solution (telling typescript to enforce that the children are ValidChildA | ValidChildB is tricky or not doable. Also, as you pointed out, doesn't solve your issue because you need it to happen at runtime. But still found it an interesting little research project. Check out the posts if you want more on that direction.
SO: How do I Restrict Type of React Children
SO: Typescript and React Children

Related

Is possible to force a component to be used inside another component in react?

I'd like to create 2 react components <Father /> and <Child /> and force the developer to use Child only inside Father:
<Father>
<!-- should be fine -->
<Child />
</Father>
<!-- should throw an error -->
<Child />
Is there any way to do it? If so, what's the syntax I should use?
You can leverage the context API to determine whether a child has a Father in parent or not.
The way you can do that is for your Father component to render a ContextProvider and your child to use the context
const FatherContext = React.createContext(null); // default value used if father not in hierarchy
const Father = ({children}) => {
// Component Logic here
return (
<FatherContext.Provider value={"Father"}>
{/* rest content */
{children}
</FatherContext.Provider>
)
}
and your Child can be like
const Child = () => {
const context = useContext(FatherContext);
if(context == null) {
console.warn("Child must be rendered inside of Father component"); // You can throw an error here too
}
return (...)
}
Now depending on which version of React you use, the implementation of Context differs but the idea remains the same
You can do something out of the box like this:
<Father>
{React.cloneElement(this.props.children, {...this.props, dna: 'uniquefather'})}
</Father>
Child class
...
if (this.props.dna !== 'uniquefather') throw new Error('something');
Here, the father will be passing an unique identifier, which if not received will throw error in child class.

How to read child elements from a React component

I want to get a list of all child elements of a react component.
For example:
Parent.js
<Parent>
<SomeComponent/>
</Parent>
SomeComponent.js
<SomeComponent>
<ChildElement1/>
<ClhidElement2/>
</SomeComponent>
So in Parent.js I want to get ChildElement1 and ChildElement2.
Is that possible?
My Use case is:
I'm passing form fields (Field component) to a generic Form component. The Form element receives an object of default values and also other things (what CRUD/resource it is related to for example). And it must inject these values in the Fields. Instead of passing all fields one by one and avoid repetion I created containers like "UserFields" and a few others and they are the ones that have the Fields components. So I need Form to read the Fields in UserFields. But since the fields are already inside UserFields, I can't figure out how to get them.
React is designed to be unidirectional data flow and following Flux architecture, and hence to keep best practices, it's always top down (from parent to child, not bidirectional).
However, you can achieve them in several options such as implementing React with redux or React Context
I am considering that your child components are mapped from array inside <SomeComponent />
Try this inside your parent
state = {
child: []
}
renderChildren = () => {
if(this.state.child.length > 0) {
return this.state.child.map(e => {
return (
<div>{e}</div>
)
})
}
}
returnChild = (data) => {
var child = [];
for(var i = 0; i < data.length; i++) {
child.push(data[i])
}
this.setState(prevState => ({child: [...prevState.child, child]}));
}
return (
<div>
<SomeComponent returnChild={(child) => this.returnChild(child)} />
{this.renderChildren()}
</div>
)
Add this method to your <SomeComponent /> component like this along with other code.
onGettingMoreChild = (child) => {
this.props.returnChild(child)
}
Don't forget to call onGettingMoreChild whenever there is a new child created.
I have not tested this code. Please playaround with it if needed. Also, remember to pass in your entire view as child to the method onGettingMoreChild
Example of child variable passed to onGettingMoreChild is
<div>I am child one!!</div>

ReactJS webpack babel: _this2 is undefined when clicking button whose function is passed via a parameter

No doubt I've missed something really obvious here but I'm trying to do the lynda tutorial "Learning React.js", it's a bit different since I'm using ES6 classes and transpiling using Babel via webpack.
The problem is executing a function being passed as a parameter to the Note class from the Board class.
I have a "Note" class which can be described as:
remove() {
this.props.onRemove(this.props.id);
}
//Notes class
render() {
return (
<div className="note">
<p>{this.props.children}</p>
<span>
<button onClick={() => this.remove() }>X</button>
</span>
</div>
);
}
Where onRemove is a function that's being passed as a prop from Board to Note like this:
//Method in Board class. I map through an array and call this function on each element to create notes.
eachNote(note) {
return (
<Note key = { note.id } id = { note.id } onRemove = { (id) => this.remove(id) }>
{ note.note }
</Note>
)
}
The remove() method in the Board class looks like this:
//Remove method in the Board class
remove(id) {
let notes = this.state.notes.filter(note => note.id !== id);
this.setState({notes});
}
When I click a Note's X button it DOES execute the local function in the note class but then DOESN'T execute the method being passed as a property (onRemove()) and instead I get (Firefox):
TypeError: _this2 is undefined
and in Chrome I get:
Cannot read property 'remove' of undefined
I don't get why it can't find the function though, I did a previous tutorial which did exactly this and it's able to find the function ok. I've probably overlooked something really simple.
[EDIT: Extra Board Info]
//Board's render method
render() {
return (
<div className='board'>
{ this.state.notes.map(this.eachNote) }
</div>
);
}
In the console it outputs what I expect.
after an afternoon of trying everything under the sun, I would like to show my solution in case anybody else had this problem.
My render method is mapping through an array of notes and creating from each element. The problem was that I wasn't passing a 'notes' object to the function which created the note element. So I fixed it by changing my render method to this:
render() {
return (
<div className='board'>
{ this.state.notes.map((note) => this.eachNote(note)) }
</div>
);
}
I'm not sure but I think this works because each Note instance has it's own this given to it. If someone has a better explanation please let me know.
If I understand correctly that the "child" component handel the click,
But the click does not propagate the the parent component (Board).
In Board, you are changing this.state (correctly) but passed along to the "chil" component, "this" changes.
Try (Board):
This.remove = this.remove.bind (this);
https://reactjs.org/docs/handling-events.html

Pass argument onClick to props.function() executed via refs

I'm sorry this title might be a bit of a head-scratcher, I just can't quite think of how exactly to phrase my issue (open to any suggestions).
Basically there are 3 components: parent, ChildA, and ChildB
Parent renders both children, ChildB has a ref tag, Parent uses that ref tag to pass ChildB's function to ChildA.
class Parent extends Component {
render() {
<ChildA openChildB={() => this.childb.open()} />
<ChildB ref={instance => { this.childb = instance; }} />
}
}
Pretty basic.
The issue I am running into is that when ChildA executes that function it needs to pass an argument. I can't seem to figure out the correct way to do that.
I tried using a different syntax to pass the function down to ChildA -
<ChildA openChildB={this.childb.open} />
but that results in an error, Can't Read Property Of Undefined.
How can I pass a variable through this function?
Any help would be very appreciated!
Edit: I know I could pass the argument up to Parent and then from there place it in the function () => this.childb.open(arg) but for the sake of organization I would really prefer to handle that all within ChildA.
You can use the following:
class Parent extends Component {
render() {
<ChildA openChildB={(arg1) => this.childb.open(arg1)} />
<ChildB ref={instance => { this.childb = instance; }} />
}
}
And inside ChildA you should make sure to pass the relevant argument when you call the this.props.openChildB:
function handleOpenOnB() {
// This is an example, you should use the relevant value you want to pass to the openChildB function
this.props.openChildB(this.state.val);
}

Pass props to parent component in React.js

Is there not a simple way to pass a child's props to its parent using events, in React.js?
var Child = React.createClass({
render: function() {
<a onClick={this.props.onClick}>Click me</a>
}
});
var Parent = React.createClass({
onClick: function(event) {
// event.component.props ?why is this not available?
},
render: function() {
<Child onClick={this.onClick} />
}
});
I know you can use controlled components to pass an input's value but it'd be nice to pass the whole kit n' kaboodle. Sometimes the child component contains a set of information you'd rather not have to look up.
Perhaps there's a way to bind the component to the event?
UPDATE – 9/1/2015
After using React for over a year, and spurred on by Sebastien Lorber's answer, I've concluded passing child components as arguments to functions in parents is not in fact the React way, nor was it ever a good idea. I've switched the answer.
Edit: see the end examples for ES6 updated examples.
This answer simply handle the case of direct parent-child relationship. When parent and child have potentially a lot of intermediaries, check this answer.
Other solutions are missing the point
While they still work fine, other answers are missing something very important.
Is there not a simple way to pass a child's props to its parent using events, in React.js?
The parent already has that child prop!: if the child has a prop, then it is because its parent provided that prop to the child! Why do you want the child to pass back the prop to the parent, while the parent obviously already has that prop?
Better implementation
Child: it really does not have to be more complicated than that.
var Child = React.createClass({
render: function () {
return <button onClick={this.props.onClick}>{this.props.text}</button>;
},
});
Parent with single child: using the value it passes to the child
var Parent = React.createClass({
getInitialState: function() {
return {childText: "Click me! (parent prop)"};
},
render: function () {
return (
<Child onClick={this.handleChildClick} text={this.state.childText}/>
);
},
handleChildClick: function(event) {
// You can access the prop you pass to the children
// because you already have it!
// Here you have it in state but it could also be
// in props, coming from another parent.
alert("The Child button text is: " + this.state.childText);
// You can also access the target of the click here
// if you want to do some magic stuff
alert("The Child HTML is: " + event.target.outerHTML);
}
});
JsFiddle
Parent with list of children: you still have everything you need on the parent and don't need to make the child more complicated.
var Parent = React.createClass({
getInitialState: function() {
return {childrenData: [
{childText: "Click me 1!", childNumber: 1},
{childText: "Click me 2!", childNumber: 2}
]};
},
render: function () {
var children = this.state.childrenData.map(function(childData,childIndex) {
return <Child onClick={this.handleChildClick.bind(null,childData)} text={childData.childText}/>;
}.bind(this));
return <div>{children}</div>;
},
handleChildClick: function(childData,event) {
alert("The Child button data is: " + childData.childText + " - " + childData.childNumber);
alert("The Child HTML is: " + event.target.outerHTML);
}
});
JsFiddle
It is also possible to use this.handleChildClick.bind(null,childIndex) and then use this.state.childrenData[childIndex]
Note we are binding with a null context because otherwise React issues a warning related to its autobinding system. Using null means you don't want to change the function context. See also.
About encapsulation and coupling in other answers
This is for me a bad idea in term of coupling and encapsulation:
var Parent = React.createClass({
handleClick: function(childComponent) {
// using childComponent.props
// using childComponent.refs.button
// or anything else using childComponent
},
render: function() {
<Child onClick={this.handleClick} />
}
});
Using props:
As I explained above, you already have the props in the parent so it's useless to pass the whole child component to access props.
Using refs:
You already have the click target in the event, and in most case this is enough.
Additionnally, you could have used a ref directly on the child:
<Child ref="theChild" .../>
And access the DOM node in the parent with
React.findDOMNode(this.refs.theChild)
For more advanced cases where you want to access multiple refs of the child in the parent, the child could pass all the dom nodes directly in the callback.
The component has an interface (props) and the parent should not assume anything about the inner working of the child, including its inner DOM structure or which DOM nodes it declares refs for. A parent using a ref of a child means that you tightly couple the 2 components.
To illustrate the issue, I'll take this quote about the Shadow DOM, that is used inside browsers to render things like sliders, scrollbars, video players...:
They created a boundary between what you, the Web developer can reach
and what’s considered implementation details, thus inaccessible to
you. The browser however, can traipse across this boundary at will.
With this boundary in place, they were able to build all HTML elements
using the same good-old Web technologies, out of the divs and spans
just like you would.
The problem is that if you let the child implementation details leak into the parent, you make it very hard to refactor the child without affecting the parent. This means as a library author (or as a browser editor with Shadow DOM) this is very dangerous because you let the client access too much, making it very hard to upgrade code without breaking retrocompatibility.
If Chrome had implemented its scrollbar letting the client access the inner dom nodes of that scrollbar, this means that the client may have the possibility to simply break that scrollbar, and that apps would break more easily when Chrome perform its auto-update after refactoring the scrollbar... Instead, they only give access to some safe things like customizing some parts of the scrollbar with CSS.
About using anything else
Passing the whole component in the callback is dangerous and may lead novice developers to do very weird things like calling childComponent.setState(...) or childComponent.forceUpdate(), or assigning it new variables, inside the parent, making the whole app much harder to reason about.
Edit: ES6 examples
As many people now use ES6, here are the same examples for ES6 syntax
The child can be very simple:
const Child = ({
onClick,
text
}) => (
<button onClick={onClick}>
{text}
</button>
)
The parent can be either a class (and it can eventually manage the state itself, but I'm passing it as props here:
class Parent1 extends React.Component {
handleChildClick(childData,event) {
alert("The Child button data is: " + childData.childText + " - " + childData.childNumber);
alert("The Child HTML is: " + event.target.outerHTML);
}
render() {
return (
<div>
{this.props.childrenData.map(child => (
<Child
key={child.childNumber}
text={child.childText}
onClick={e => this.handleChildClick(child,e)}
/>
))}
</div>
);
}
}
But it can also be simplified if it does not need to manage state:
const Parent2 = ({childrenData}) => (
<div>
{childrenData.map(child => (
<Child
key={child.childNumber}
text={child.childText}
onClick={e => {
alert("The Child button data is: " + child.childText + " - " + child.childNumber);
alert("The Child HTML is: " + e.target.outerHTML);
}}
/>
))}
</div>
)
JsFiddle
PERF WARNING (apply to ES5/ES6): if you are using PureComponent or shouldComponentUpdate, the above implementations will not be optimized by default because using onClick={e => doSomething()}, or binding directly during the render phase, because it will create a new function everytime the parent renders. If this is a perf bottleneck in your app, you can pass the data to the children, and reinject it inside "stable" callback (set on the parent class, and binded to this in class constructor) so that PureComponent optimization can kick in, or you can implement your own shouldComponentUpdate and ignore the callback in the props comparison check.
You can also use Recompose library, which provide higher order components to achieve fine-tuned optimisations:
// A component that is expensive to render
const ExpensiveComponent = ({ propA, propB }) => {...}
// Optimized version of same component, using shallow comparison of props
// Same effect as React's PureRenderMixin
const OptimizedComponent = pure(ExpensiveComponent)
// Even more optimized: only updates if specific prop keys have changed
const HyperOptimizedComponent = onlyUpdateForKeys(['propA', 'propB'])(ExpensiveComponent)
In this case you could optimize the Child component by using:
const OptimizedChild = onlyUpdateForKeys(['text'])(Child)
Update (9/1/15): The OP has made this question a bit of a moving target. It’s been updated again. So, I feel responsible to update my reply.
First, an answer to your provided example:
Yes, this is possible.
You can solve this by updating Child’s onClick to be this.props.onClick.bind(null, this):
var Child = React.createClass({
render: function () {
return <a onClick={this.props.onClick.bind(null, this)}>Click me</a>;
}
});
The event handler in your Parent can then access the component and event like so:
onClick: function (component, event) {
// console.log(component, event);
},
JSBin snapshot
But the question itself is misleading
Parent already knows Child’s props.
This isn’t clear in the provided example because no props are actually being provided. This sample code might better support the question being asked:
var Child = React.createClass({
render: function () {
return <a onClick={this.props.onClick}> {this.props.text} </a>;
}
});
var Parent = React.createClass({
getInitialState: function () {
return { text: "Click here" };
},
onClick: function (event) {
// event.component.props ?why is this not available?
},
render: function() {
return <Child onClick={this.onClick} text={this.state.text} />;
}
});
It becomes much clearer in this example that you already know what the props of Child are.
JSBin snapshot
If it’s truly about using a Child’s props…
If it’s truly about using a Child’s props, you can avoid any hookup with Child altogether.
JSX has a spread attributes API I often use on components like Child. It takes all the props and applies them to a component. Child would look like this:
var Child = React.createClass({
render: function () {
return <a {...this.props}> {this.props.text} </a>;
}
});
Allowing you to use the values directly in the Parent:
var Parent = React.createClass({
getInitialState: function () {
return { text: "Click here" };
},
onClick: function (text) {
alert(text);
},
render: function() {
return <Child onClick={this.onClick.bind(null, this.state.text)} text={this.state.text} />;
}
});
JSBin snapshot
And there's no additional configuration required as you hookup additional Child components
var Parent = React.createClass({
getInitialState: function () {
return {
text: "Click here",
text2: "No, Click here",
};
},
onClick: function (text) {
alert(text);
},
render: function() {
return <div>
<Child onClick={this.onClick.bind(null, this.state.text)} text={this.state.text} />
<Child onClick={this.onClick.bind(null, this.state.text2)} text={this.state.text2} />
</div>;
}
});
JSBin snapshot
But I suspect that’s not your actual use case. So let’s dig further…
A robust practical example
The generic nature of the provided example is a hard to talk about. I’ve created a component that demonstrations a practical use for the question above, implemented in a very Reacty way:
DTServiceCalculator working example
DTServiceCalculator repo
This component is a simple service calculator. You provide it with a list of services (with names and prices) and it will calculate a total the selected prices.
Children are blissfully ignorant
ServiceItem is the child-component in this example. It doesn’t have many opinions about the outside world. It requires a few props, one of which is a function to be called when clicked.
<div onClick={this.props.handleClick.bind(this.props.index)} />
It does nothing but to call the provided handleClick callback with the provided index[source].
Parents are Children
DTServicesCalculator is the parent-component is this example. It’s also a child. Let’s look.
DTServiceCalculator creates a list of child-component (ServiceItems) and provides them with props [source]. It’s the parent-component of ServiceItem but it`s the child-component of the component passing it the list. It doesn't own the data. So it again delegates handling of the component to its parent-component source
<ServiceItem chosen={chosen} index={i} key={id} price={price} name={name} onSelect={this.props.handleServiceItem} />
handleServiceItem captures the index, passed from the child, and provides it to its parent [source]
handleServiceClick (index) {
this.props.onSelect(index);
}
Owners know everything
The concept of “Ownership” is an important one in React. I recommend reading more about it here.
In the example I’ve shown, I keep delegating handling of an event up the component tree until we get to the component that owns the state.
When we finally get there, we handle the state selection/deselection like so [source]:
handleSelect (index) {
let services = […this.state.services];
services[index].chosen = (services[index].chosen) ? false : true;
this.setState({ services: services });
}
Conclusion
Try keeping your outer-most components as opaque as possible. Strive to make sure that they have very few preferences about how a parent-component might choose to implement them.
Keep aware of who owns the data you are manipulating. In most cases, you will need to delegate event handling up the tree to the component that owns that state.
Aside: The Flux pattern is a good way to reduce this type of necessary hookup in apps.
It appears there's a simple answer. Consider this:
var Child = React.createClass({
render: function() {
<a onClick={this.props.onClick.bind(null, this)}>Click me</a>
}
});
var Parent = React.createClass({
onClick: function(component, event) {
component.props // #=> {Object...}
},
render: function() {
<Child onClick={this.onClick} />
}
});
The key is calling bind(null, this) on the this.props.onClick event, passed from the parent. Now, the onClick function accepts arguments component, AND event. I think that's the best of all worlds.
UPDATE: 9/1/2015
This was a bad idea: letting child implementation details leak in to the parent was never a good path. See Sebastien Lorber's answer.
The question is how to pass argument from child to parent component. This example is easy to use and tested:
//Child component
class Child extends React.Component {
render() {
var handleToUpdate = this.props.handleToUpdate;
return (<div><button onClick={() => handleToUpdate('someVar')}>Push me</button></div>
)
}
}
//Parent component
class Parent extends React.Component {
constructor(props) {
super(props);
var handleToUpdate = this.handleToUpdate.bind(this);
}
handleToUpdate(someArg){
alert('We pass argument from Child to Parent: \n' + someArg);
}
render() {
var handleToUpdate = this.handleToUpdate;
return (<div>
<Child handleToUpdate = {handleToUpdate.bind(this)} />
</div>)
}
}
if(document.querySelector("#demo")){
ReactDOM.render(
<Parent />,
document.querySelector("#demo")
);
}
Look at JSFIDDLE
Basically you use props to send information to and from Child and Parent.
Adding to all the wonderful answers, let me give a simple example that explains passing values from child to parent component in React
App.js
class App extends React.Component {
constructor(){
super();
this.handleFilterUpdate = this.handleFilterUpdate.bind(this);
this.state={name:'igi'}
}
handleFilterUpdate(filterValue) {
this.setState({
name: filterValue
});
}
render() {
return (
<div>
<Header change={this.handleFilterUpdate} name={this.state.name} />
<p>{this.state.name}</p>
</div>
);
}
}
Header.js
class Header extends React.Component {
constructor(){
super();
this.state={
names: 'jessy'
}
}
Change(event) {
// this.props.change(this.state.names);
this.props.change('jessy');
}
render() {
return (
<button onClick={this.Change.bind(this)}>click</button>
);
}
}
Main.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App.jsx';
ReactDOM.render(<App />, document.getElementById('app'));
Thats it , now you can pass values from your client to the server.
Take a look at the Change function in the Header.js
Change(event) {
// this.props.change(this.state.names);
this.props.change('jessy');
}
This is how you push values into the props from client to the server
Here is a simple 3 step ES6 implementation using function binding in the parent constructor. This is the first way the official react tutorial recommends (there is also public class fields syntax not covered here). You can find all of this information here https://reactjs.org/docs/handling-events.html
Binding Parent Functions so Children Can Call Them (And pass data up to the parent! :D )
Make sure in the parent constructor you bind the function you created in the parent
Pass the bound function down to the child as a prop (No lambda because we are passing a ref to function)
Call the bound function from a child event (Lambda! We're calling the function when the event is fired.
If we don't do this the function will automatically run on load and not be triggered on the event.)
Parent Function
handleFilterApply(filterVals){}
Parent Constructor
this.handleFilterApply = this.handleFilterApply.bind(this);
Prop Passed to Child
onApplyClick = {this.handleFilterApply}
Child Event Call
onClick = {() => {props.onApplyClick(filterVals)}
This is an example without using the onClick event. I simply pass a callback function to the child by props. With that callback the child call also send data back. I was inspired by the examples in the docs.
Small example (this is in a tsx files, so props and states must be declared fully, I deleted some logic out of the components, so it is less code).
*Update: Important is to bind this to the callback, otherwise the callback has the scope of the child and not the parent. Only problem: it is the "old" parent...
SymptomChoser is the parent:
interface SymptomChooserState {
// true when a symptom was pressed can now add more detail
isInDetailMode: boolean
// since when user has this symptoms
sinceDate: Date,
}
class SymptomChooser extends Component<{}, SymptomChooserState> {
state = {
isInDetailMode: false,
sinceDate: new Date()
}
helloParent(symptom: Symptom) {
console.log("This is parent of: ", symptom.props.name);
// TODO enable detail mode
}
render() {
return (
<View>
<Symptom name='Fieber' callback={this.helloParent.bind(this)} />
</View>
);
}
}
Symptom is the child (in the props of the child I declared the callback function, in the function selectedSymptom the callback is called):
interface SymptomProps {
// name of the symptom
name: string,
// callback to notify SymptomChooser about selected Symptom.
callback: (symptom: Symptom) => void
}
class Symptom extends Component<SymptomProps, SymptomState>{
state = {
isSelected: false,
severity: 0
}
selectedSymptom() {
this.setState({ isSelected: true });
this.props.callback(this);
}
render() {
return (
// symptom is not selected
<Button
style={[AppStyle.button]}
onPress={this.selectedSymptom.bind(this)}>
<Text style={[AppStyle.textButton]}>{this.props.name}</Text>
</Button>
);
}
}

Categories