I would like to know how to inject a property an object of a component.
Below is the App Container component code
<FormItem element={<FormItemText />} />
Below are the 2 component.
FormItem extends Component {
saysomething(){
console.log("Saying something")
}
render(){
return (
{this.props.element}
)
}
}
and
FormItemText extends Component {
render(){
return (
<TextInput onChangeText={(text) => this.props.saysomething(text)}
)
}
}
How do I pass along the FormItem.saysomething as a property to FormItemText component.
Thanks
You have to use direct manipulation to inject a property programatically.
First add a ref attribute to your FormItemText component.
Create a function to call setNativeProps on your TextInput.onChangeText.
Done!
Note that direct manipulation is computationally intensive - React needs to re-render the component hierarchy each time the opacity changes, even though other properties of the view and its children haven't changed. Usually this overhead isn't a concern but when performing continuous animations and responding to gestures, judiciously optimizing your components can improve your animations' fidelity.
For more information and examples, read React Native Documentation here.
We do something similar to what you're asking.
We have a Form component that accepts a number of FormInputs as children:
<Form>
<FormInput />
<FormInput />
</Form>
The Form needs to give some functions and properties to the FormInput. For example, the FormInput may need to scroll the parent Form when it is focused.
We do this using a React context
https://facebook.github.io/react/docs/context.html
In your example, it would end up looking something like this:
<FormItem>
<FormItemText />
</FormItem>
FormItem extends Component {
getChildContext() {
return { saysomething: this.saysomething };
}
saysomething(){
console.log("Saying something")
}
render(){
return (
{this.props.children}
)
}
}
FormItem.childContextTypes = {
saysomething: PropTypes.func,
};
FormItemText extends Component {
render(){
return (
<TextInput onChangeText={(text) => this.context.saysomething(text)}
)
}
}
FormItemText.contextTypes = {
saysomething: PropTypes.func,
};
Related
I want to create multiple ref's to the same global component. Like for example TextInput , if i want to add ref to it i use. this in my class based component :
<TextInput
ref={input => {
this.secondTextInput = input;
}}
/>
and then call via this.secondTextInput.focus(); to focus the textinput. It works as long im importing the whole textInput directly in my class.
Similiarly if ive created a global component for TextInput in another file like :
export const OTPInput = props => {
return (
<TextInput
placeholder={props.title}
onChangeText={props.onTextEnter}
value={props.value}
/>
);
};
and using this in my classes by importing like :
Class ABC extends Component{
render(){
return(
<>
<OTPInput title ="first otp" />
<OTPInput title ="another otp" />
</>
)
}
}
How can i create ref and pass it , so that i can focus the text input by clicking some button in my class func .
Any help would be greatful
Forwarding Refs
export const OTPInput = React.forwardRef((props, ref) => (
<TextInput
ref={ref}
placeholder={props.title}
onChangeText={props.onTextEnter}
value={props.value}
/>
));
Then as per your example usage, just create a ref and attach
const OTPInputRef1 = React.createRef();
...
<OTPInput ref={OTPInputRef1} title ="first otp" />
You should take a look at Forwarding refs described here in the react documents
https://reactjs.org/docs/forwarding-refs.html
So, I have a custom, configurable input component which is supposed to be used in few different forms. The "configuration" for the input filed is being handled by the parent components (validations, type of input field, etc.).
The problem is the event type. For example, in one of the forms, I need the event type on the input field to be onBlur and in another form - onKeyDown.
The question is: how can I pass the event type from the parent component which renders the custom input filed?
Disclaimer - I am new to React, quite new to programming and have been looking for a solution for a few days now to no avail.
First, define your component, that holds the <input /> (or function)
class MyClass extends Component {
render() {
// you can extract some of the props if you want to manipulate somehow. Everything else will be saved in rest
const { value, name, ...rest } = this.props;
return (
<input value={value} name={name} {...rest} />
)
}
}
Then, to that new component, you can add any props, that are available for <input/> tag, example:
class Test extends Component {
render() {
return (
<Fragment>
<MyClass value={5} name="Name" onBlur={this.onBlur} onFocus={this.onFocus} />
<MyClass value="Hello, world!" id='randomId' onChange={e => console.log(e.target.value)} />
</Fragment>
)
}
onBlur = e => {
console.log('Blur');
}
onFocus = e => {
console.log('Focus');
}
}
Now you have two different inputs - one is listening for onBlur and onFocus, the other one has id and listens for onChange.
I have a parent component and a child component as following.
Parent component:
constructor(){
this.refs = React.createRef();
}
setRef(ref) {
console.log(ref)
}
handleChange(e) {
console.log(e)
}
render() {
return(
<ChildComponent ref={this.setRef} handleChange={this.handleChange.bind(this)}/>
)
}
Child Component:
render() {
return(
<input type="text" ref={this.props.ref} onChange={this.props.handleChange.bind(this)} > </input>
)
}
What changes should I do to get the ref value inside the handleChange() function in parent component? Thanks in advance.
If I´m reading correctly you want to access the input element from the parent component?
You have to use another name for your prop as ref is a keyword prop which will automatically assign the component the given variable.
<ChildComponent inputRef={this.setRef} handleChange={this.handleChange.bind(this)}/>
class ChildComponent extends Component {
render() {
return(
<input type="text" ref={this.props.inputRef} onChange= {this.props.handleChange.bind(this)} > </input>
);
}
}
Based on how you want to access the ref you can either set this.refs directly to the prop or set it inside your setRef function.
// either `this.refs` and then usable through `this.refs.current`
<ChildComponent inputRef={this.refs} {...} />
// or `this.setRef` and assign it yourself
setRef = (ref) => {this.refs = ref;}
ref(as well as key btw) is very special prop. It is not accessible in child by this.props.ref.
Shortest way is to use different prop to pass ref forward and backward:
class Parent ...
render() {
...
<Child inputRef={this.inputRef} />
class Child
...
render() {
<input ref={this.props.inputRef} ...
It's most flexible since you may access different ref inside you child component(e.g. inputRef + scrollableContainerRef + popupRef)
But in some cases you want to compose new component for existing code base. Say <input>'s replacement. Sure, in this case you would avoid changing all <input ref={...} /> onto <MyInput refProp={...}>.
Here you may use React.forwardRef. It feels the best with functional components like
export Child = React.forwardRef((props, forwardedRef) => {
...
return ...
<input ref={forwardedRef} />
})
But for class-based component you would rather use ref-prop with different name:
class Child
...
render()
return
...
<input ref={this.props.forwardedRefName} />
export ChildWithForwardRef = React.forwardRef((props, ref) => <Child forwardedRefName={ref} {...props} />)
PS since you will consume what forwardRef returns rather initial component(Child) you may want to specify displayName for it. This way you may be able find it later say with enzyme's find() or easy recognize element in browser's React DevTools
ref work only on native dom element, to make ref work on user defined Component, you need this:
https://reactjs.org/docs/forwarding-refs.html
I have some text. When you click on that element a modal pops up that lets you edit that text. The easiest way to make this work is to call setState on the child to initialise the text.
The other way, although more awkward, is to create an initial text property and make the child set it's text based on this.
Is there anything wrong with directly calling setState on the child or should I use the second method?
Although it is recommended to keep the data of your react application "up" in the react dom (see more here https://reactjs.org/docs/lifting-state-up.html), I don't see anything wrong with the first aproach you mentioned.
If you have to store data that is very specific of a child I don't see anything wrong in keep that information in the child's state.
It seems that your modal doesn't need to have its own state, in which case you should use a stateless React component.
This is one way of passing the data around your app in the React way.
class ParentComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
initialText: "hello",
}
this.saveChildState = this.saveChildState.bind(this);
}
saveChildState(input) {
console.log(input);
// handle the input returned from child
}
render() {
return (
<div>
<ChildComponent
initialText={this.state.initialText}
save={this.saveChildState}
/>
</div>
);
}
}
function ChildComponent(props) {
return (
<div>
<input id="textInput" type="text" defaultValue={props.initialText}>
</input>
<button onClick={() => props.save(document.getElementById('textInput').value)}>
Save
</button>
</div>
)
}
Maybe I am misinterpreting your question, but I think it would make the most sense to keep the modal text always ready in your state. When you decide to show your modal, the text can just be passed into the modal.
class Test extends Component {
constructor() {
this.state = {
modalText: 'default text',
showModal: false
}
}
//Include some method to change the modal text
showModal() {
this.setState({showModal: true})
}
render(
return (
<div>
<button onClick={() => this.showModal()}>
Show Modal
</button>
{ this.state.showModal ? <Modal text={this.state.modalText}/> : null }
</div>
)
)
}
In my case I try to create a simple Form Component - mostly for "testing" reactjs and work with it.
To do this I work with 2 Components. The first Component is the Parent, the "Form" Component. The second Component is the field of the form - for example a simple textfield. This is the markup it would look like:
<Form
schema={MyFormSchema}
>
<Input name="name" />
<Input name="age" />
</Form>
In MyFormSchema I have all information which I need for every Child of the type "Input". For this case I have done this in my "Form" Component:
Form.jsx
Form = React.createClass({
renderChildren() {
return React.Children.map(this.props.children, (child)=>{
if (child.type && child.type.prototype && child.type.prototype.constructor.displayName === 'Input') {
let additionalProps = {
fieldSchema: this.props.schema.pick(child.props.name),
onChange: this.onChange
};
return React.cloneElement(child, additionalProps);
}
return child;
});
},
render() {
return(
<form>
{this.renderChildren()}
</form>
);
}
});
What I am doing here is to "clone" every "input" child and add some new props depending on the schema.
So the first question is:
Is this really the correct war in reactJs ? When I am not "cloning" every element and adding new properties I have to add the property directly in my View, right ? Something like but I am trying to prevent this because all information I need I already have as a prop in my Form Schema.
After playing around with this I found out, that this.props.children only have the first level of children. But when I have nested my Children in my Form Component it will not work anymore that my Component is replacing the Input Component with the manipulated component.
Example:
<Form
schema={MyFormSchema}
>
<AnotherComponent>
<Input name="name" />
</AnotherComponent>
<Input name="age" />
</Form>
When I am doing it like I now doing it this code will not work anymore because in this.props.children I only have [AnotherComponent, Input[name=age]] and the Input[name=name] is missing. So I think the way I am doing it is the wrong way. But i am not sure.
So the main question is:
Like in my example: What is the correct way in ReactJs to inherit props (or what ever) to all children (also the nested one) - or is this not possible in the "react" way and I really have to pass all necessary props to all children ?
Edit:
When I am talking about "pass all necessary props to all children" I mean something like this:
<Form
schema={MyFormSchema}
>
<AnotherComponent>
<Input name="name" fieldSchema={this.getFieldSchema('name')} onChange={this.onChange} />
</AnotherComponent>
<Input name="age" fieldSchema={this.getFieldSchema('age')} onChange={this.onChange} />
</Form>
In this example I would pass all necessary props I want to add dynamically by the parent. In my example above the next problem would be: "this" would not work for the name input because of its parent AnotherComponent. So I would have to reference to the parent - of course: its possible, but I think it would be a ugly way.
There are three correct ways to deeply pass props:
1) Just actually pass them down the tree from each component to the next (this is the most readable (in terms of code logic), but can get unwieldy once you have too many props to pass and lots of levels in your tree.
Example:
import React from 'react';
var GrandParent = React.createClass({
render () {
return (
<Parent familyName={'componentFamily'} />
);
}
});
var Parent = React.createClass({
render () {
return (
<Child familyName={props.familyName} />
);
}
});
var Child = React.createClass({
render () {
return (
<p>{'Our family name is ' + props.familyName}</p>
);
}
});
2) Use a Flux-style store (I prefer Reflux, though Redux is all the rage right now) to keep a common state. All components can then access that store. For me at least, this is the current preferred method. It's clear and it keeps business logic out of the components.
Example (using Reflux):
import React from 'react';
import Reflux from 'reflux';
var MyStore = Reflux.createStore({
// This will be called in every component that connects to the store
getInitialState () {
return {
// Contents of MyFormSchema here
};
}
});
var Input = React.createClass({
propTypes: {
name: React.PropTypes.string.isRequired
},
mixins: [Reflux.connect(MyStore)],
render () {
// I don't know what MyFormSchema so I'm generalizing here, but lets pretend its a map that uses the name of each field a key and then has properties of that field within the map stored at the key/value
return (
<input type={this.state[name].type} name={this.props.name} value={this.state[name].type} />
);
}
});
3) Use React's context feature. As you'll see immediately from looking at the docs, this feature is still in development and is subject to possible change and even removal in future versions of React. So, while it is likely the easiest way to pass props down a tree of components, personally I'm staying away from it until it becomes more of a finalized feature.
I'm not going to write an example for this one since the docs make it very clear. However, make sure to scroll down on the doc page and take a look at Parent-child coupling, which is kind of what you're doing right now.
Another solution for you is that instead of having a single component that renders Form and its Inputs, why not just pass the prop to Form as you do currently, and then simply render the individual Input using Form's render().
You could use react-addons-clone-with-props package this way:
import React, { Component } from 'react';
import cloneWithProps from 'react-addons-clone-with-props';
// ...
class Form extends Component {
recursivelyMapChildren(children) {
return React.Children.map(children, child => {
if (!React.isValidElement(child)) {
return child;
}
return React.cloneElement(child, {
...child.props,
children: this.recursiveCloneChildren(child.props.children)
});
})
}
render() {
return (
<form>{this.recursivelyMapChildren(this.props.children)}</form>
);
}
}
What the code does:
Gets all the children components via predefined children prop (see docs).
Recursively maps the collection of children with React.Children.map method, applying a lambda function to each element.
Saves the mapped (i.e. updated, but not mutated!) children elements into mappedChildren constant.
Puts them within form DOM element.
It looks simple and it should be so.
But you have to keep in mind that React is great when your code is kept clean and transparent. When you explicitly pass props like
<Form
schema={MyFormSchema}
>
<Input
name="name"
schema={MyFormSchema} />
<Input
name="age"
schema={MyFormSchema} />
</Form>
there's way less things to get broken when you accidentally change the underlying logic.
Thankyou. Credits #Rishat Muhametshin
I have used the above to create a re-usable method.
This works beautifully:
utils/recursivelyMapChildren.jsx
const recursivelyMapChildren = (children, addedProperties) => {
return React.Children.map(children, child => {
if (!React.isValidElement(child)) {
return child;
}
return React.cloneElement(child, {
...child.props,
...addedProperties,
children: this.recursivelyMapChildren(child.props.children, addedProperties)
});
})
};
export default recursivelyMapChildren;
usecase:
Form.jsx
import recursivelyMapChildren from 'utils/recursivelyMapChildren';
class Form extends Component {
handleValidation(evt, name, strValidationType){
/* pass this method down to any nested level input field */
}
render(){
return (
<form>
{recursivelyMapChildren(this.props.children, {
handleValidation: this.handleValidation.bind(this)
})}
<input type="submit" value="submit" className="validation__submit"/>
</form>
)
}
}
export default Form
SomeExample.jsx
const SomeExample = () => {
return (
<Form>
<input type="hidden" name="_method" value="PUT"/>
<fieldset>
<legend>Personal details</legend>
<div className="formRow">
<InputText/> {/* This component will receive the method - handleValidation, so it is possible to update the state on the nested grand parent - form */}
</div>
<div className="formRow">
<InputText/>{/* This component will receive the method - handleValidation, so it is possible to update the state on the nested grand parent - form */}
</div>
</fieldset>
</Form>
)
}
export default SomeExample;
I have an alternate solution to pass props to nested children. The function createFormComponents takes the schema and produces an object of components that will receive props as usual but with the schema already provided. You could link the FormContainer in my example up to a store or use setState to handle changes to the schema over time and the children will update correctly.
The example's output is to the console to demonstrate that the props are received as expected.
function Form_(props) {
console.log('Form props', props)
return <div>{props.children}</div>
}
function Input_(props) {
console.log('Input props', props)
return <div />
}
function createFormComponents(schema) {
return {
Form: props => {
return Form_({ ...props, schema })
},
Input: props => {
return Input_({ ...props, schema })
},
}
}
const FormContainer = React.createClass({
render: function() {
const myFormSchema = { x: 0, y: 1, z: 2 }
const {
Form,
Input,
} = createFormComponents(myFormSchema)
return (
<Form>
<Input name="name" />
<Input name="age" />
</Form>
)
}
})
ReactDOM.render(
<FormContainer />,
document.getElementById('container')
)
Fiddle: Props Example