ReactJS - Assign context/owner in 13.x - javascript

I am trying to render a child element that has already been initialized and is passed through a prop. My child depends on its context, yet I don't know how to apply that context before a render. Example:
http://jsfiddle.net/5qyqceaj/1/ (code below on React 0.13.3):
var Parent = React.createClass({
childContextTypes: {
test: React.PropTypes.string
},
getChildContext: function() {
return { test: "Working :)" };
},
render: function() {
return (
<div>
<b>Initialized Globally:</b><br/> {this.props.child}
<hr/>
<b>Initialized Inline:</b><br/> <Child/>
</div>
);
}
});
var Child = React.createClass({
contextTypes: {
test: React.PropTypes.string
},
render: function() {
return <div><h1>Context: {this.context.test || "Broken"}</h1></div>;
}
});
var ChildInstance = <Child/>;
React.render(<Parent child={ChildInstance} />, document.getElementById('container'));
In the example above, <Child/> when initialized globally fails to inherit the context passed by Parent.
According to https://github.com/facebook/react/issues/3451#issuecomment-104978224, this is an open issue with React 0.13.x where context is currently assigned via the owner and by 0.14 they will have it inherited from parents.
There are 3 ways I can imagine solving this:
Find a way to assign context within a component upon render or manually switch the owner of an element.
Reconstruct elements by passing in props and tagnames
Wait for 0.14
2 is really un-maintainable for more complicated structures and 3 is the surrender, is there any ways I can achieve 1?

You can use React.cloneElement to re-assign the owner of a React element. It will return a new element. Note that you will need to pass a new ref property, otherwise the previous owner will be preserved.
In your case, you would clone this.props.child in the render method of Parent.

Related

Vue Watcher not working on component created with Vue.extend

I have a Parent component with a select input which is bound through v-model to a variable in data.
Besides, I create child components dynamically using Vue.extend, which i pass the propsData which also includes the value of the select.
This components have a watcher for the prop that is related to the select input.
When i create the component it receives the props succesfully, The problem comes when I update the value of the select input that doesn't trigger the watcher on the child component.
I've been looking for similar situations but have not found something that helps me solve this problem, i don't know why it doesn't trigger the watcher on the child component when the select input changes.
Any help would be very preciated.
Here i create the component dynamically:
let PresupuestoFormularioVue = Vue.extend(PresupuestoFormulario)
let instance = new PresupuestoFormularioVue({
propsData: {
//The prop related to select input
seguro: this.seguro,
}
})
instance.$mount()
this.$refs.formularioContenedor.appendChild(instance.$el)
And this is the watcher in the component which isn't working:
watch:{
seguro:{
handler: function( newVal ){
console.log(newVal)
},
},
},
It's not the watch that doesn't work. It's the bindings. You're assigning the current value of this.seguro, not the reactive object itself. However, a new Vue() can add this binding for you.
As a sidenote, whether PresupuestoFormulario is a Vue.extend() doesn't matter. It can be any valid VueConstructor: a Vue.extend(), Vue.component() or a valid SFC (with name and template): export default {...}.
Here's how to do it:
methods: {
addPresupuestoFormulario() {
const div = document.createElement('div');
this.$el.appendChild(div);
new Vue({
components: { PresupuestoFormulario },
render: h => h("presupuesto-formulario", {
props: {
seguro: this.seguro
}
})
}).$mount(div)
}
}
The <div> initially appended to the parent will get replaced upon mounting with the actual template of PresupuestoFormulario and the bindings will be set, exactly as if you had <presupuesto-formulario :seguro="seguro" /> in the parent template from the start.
The really cool part about it is that the parent component doesn't need to have PresupuestoFormulario declared in its components.
Here's a working example:
const Test = Vue.component('test', {
template: `<div>message: {{message}}</div>`,
props: ['message'],
watch: {
message: console.log
}
})
new Vue({
el: '#app',
data: () => ({
msg: "¯\\_(ツ)_/¯"
}),
methods: {
addComponent() {
const div = document.createElement("div");
this.$el.appendChild(div);
new Vue({
components: {
Test
},
render: h => h("test", {
props: {
message: this.msg
}
})
}).$mount(div);
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue#2"></script>
<div id="app">
<input v-model="msg">
<button #click="addComponent">Add dynamic child</button>
</div>
A separate note, about using this.$el.appendChild(). While this works when you're using a root Vue instance (a so-called Vue app), it will likely fail when using a normal Vue component, as Vue2 components are limited to having only 1 root element.
It's probably a good idea to have an empty container (e.g: <div ref="container" />) in the parent, and use this.$refs.container.appendChild() instead.
All of props that you want check in watcher, should be a function. If you want read more about this go to vue document codegrepper.
watch: {
// whenever seguro changes, this function will run
seguro: function (newValue, oldValue) {
console.log(newValue,oldValue)
}
}

If render functions should be pure, how the view can get changed based on state?

I am new to react and while going through the tutorials I found this ,
"The render() function should be pure, meaning that it does not modify component state, it returns the same result each time it's invoked, and it does not directly interact with the browser." - https://facebook.github.io/react/docs/react-component.html#reference
I am little confused with this. If render function should return same result each time how can I modify the display based on states ?
For example I have text with edit button. When I click on edit, text-box should appear with content of text and edit button changes to save.
"The render() function should be pure, meaning that it does not modify component state, it returns the same result each time it's invoked, and it does not directly interact with the browser." - https://facebook.github.io/react/docs/react-component.html#reference
is absolutely a correct statement in react. Changing a state cannot be done can't inside a render method. You can access the state inside the render but change. To change the state we use setState function in callback method which set the new state and ready to change anywhere in your component.
Note: setState expects the state to be initialized first. getInitialState is the function for the same and given in example below
eg.
var firstName = 'Anand';
var testExample = React.createClass({
getDefaultProps: function () {
return {
name: 'React',
message: 'This is the default message!'
};
},
getInitialState: function () {
return {
name: this.props.name
};
},
handleNewName: function (name) {
this.setState({
name: name
});
},
render: function () {
var name = this.state.name;
var message = this.props.message;
return (
<div>
<h1>{message}</h1>
<h2>{name}</h2>
</div>
);
}
});
Inside the handleNewName function we have changed the state which then used inside render. Hope this helps

Is passing the "this" context through props an anti-pattern?

I have two components, a parent and a child like so:
class Parent extends React.Component {
shuffle() {
...
}
blur() {
...
}
next() {
...
}
previous() {
...
}
render() {
return (
<Child Parent={this} />
);
}
}
class Child extends React.Component {
constructor(props) {
super();
this.state = {};
this._onShuffleClick = this._onShuffleClick.bind(props.Parent);
this._onPreviousClick = this._onPreviousClick.bind(props.Parent);
this._onNextClick = this._onNextClick.bind(props.Parent);
}
_onShuffleClick(event) {
event.preventDefault();
this.shuffled ? this.shuffle(false) : this.shuffle(true); // I can call parents method here as the 'this' context is the 'Parent'.
this.blur(event.target);
this.setState({test "test"}); //I can set the parents state here
}
_onPreviousClick(event) {
event.preventDefault();
this.previous();
this.blur(event.target);
}
_onNextClick(event) {
event.preventDefault();
this.next();
this.blur(event.target);
}
render() {
return (
<a className="shuffle" key={1} onClick={this._shuffleOnClick}>{this.props.Parent.props.html.shuffle}</a>,
<a className="previous" key={2} onClick={this._previousOnClick}>{this.props.Parent.props.html.previous}</a>,
<a className="next" key={3} onClick={this._nextOnClick}>{this.props.Parent.props.html.next}</a>,
);
}
}
Is passing the context ('this' keyword) as a prop an anti-pattern?
Is setting the state of the parent from the child bad?
If I do this I then don't have to pass a lot of individual props to the child and I can also set the state of the parent from the child.
You can interact with the state of a parent from a child-component, but probably not the way you are trying to achieve this.
If you want to send in all props of the parent down to a child, you can do:
<Child {...this.props} />
This way, you don't need to specify each individual prop one at a time; instead, you just send them all in. Check out the spread operator here and here for more info. More info also on MDN:
The spread syntax allows an expression to be expanded in places where multiple arguments (for function calls) or multiple elements (for array literals) or multiple variables (for destructuring assignment) are expected.
If you want to access or modify the state of a parent from a child you have to do this slightly differently. Typically, you would create a function that does this interaction with the state in your parent and then send that function as a prop down to the child. Like this:
Parent:
_modifyState = (bar) => {
this.setState({foo: bar});
}
.....
<Child modifyState={this._modifyState} />
Child:
this.props.modifyState("Hello world!");
The above will set state.foo in the parent to the string Hello world! from the child component.
If you want access to all state variables, you could send it in as a prop to the child (the whole object) and then have a function (like above) which modifies the entire state (not just one property) - depends what you want really.
Well, it's mainly a bad usage of passing around the props, you could also go for {...props} instead, and I wouldn't want to pass it through the full name, you can also use let { props } = this; let parentProps = props.Parent.props. The question is also, why would you refer to parent props, that seems the bad practise, divide and conquor, only pass the props that are really needed, and do not assume in your child components that a certain parent component is available
When you pass event handlers down, let those eventhandlers be bound to your current this, but don't bind them in the child to an expected parent, a bit like this example
var StyledButton = React.createClass({
propTypes: {
clickHandler: React.PropTypes.func.Required,
text: React.PropTypes.string.required
},
render: function() {
let { clickHandler, text } = this.props;
return <button type="button" onClick={clickHandler}>{text}</button>;
}
});
var MyForm = React.createClass({
click: function() {
alert('ouch');
},
render: function() {
return <fieldset>
<StyledButton clickHandler={this.click} text="Click me" />
</fieldset>
}
})
ReactDOM.render(
<MyForm />,
document.getElementById('container')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="container">
<!-- This element's contents will be replaced with your component. -->
</div>
Yes I do think your code is bad practice. Now you chid components know about the parent component which makes your child impure.
When your parent implementation changes, the child components will break because of this.props.Parent.props.html.previous}.
I think each react component should update the parent by calling the parents functions passed by the props.
class Parent extends React.Component {
doSomethingBeacauseTheChildStateHasChanged() {
// function
}
render() {
<Child doSomething={doSomethingBeacauseTheChildStateHasChanged.bind(this)}/>
}
}
class Child extends React.Component {
render() {
<button onClick={this.props.doSomething}>Child button</button>
}
}
Note: I am not an expert and React beginner, treat this as an opinion rather than guideline.
I think yes cause you force particular implementation. What would you do if you wanted to have those methods in GrandParent? If you use props this modification is really easy, but with your implementation it would be pain in the ass.
There is also a feature called PropTypes. It's really great to make components reusable, but it's yet another thing you can't use if you do the things like you have proposed.
Maybe it is just me but this also creates a great confusion. You should pass everything you need as props.
Also setting parent state like this
this.setState({test "test"}); //I can set the parents state here
seems bad to me. I would rather pass a function from parent as a prop and bind parent before passing it down.
You can trigger a function in the Parent. This is the correct way to a children communicates with its parent.
class Parent extends React.Component {
shuffle(e) {
console.log(e.target);
return false;
}
render() {
return (
<Child onShuffle={this.shuffle} />
);
}
}
class Child extends React.Component {
render() {
return(
<a href='#' onClick={this.props.onShuffle}>Shuffle</a>
);
}
}
Child.propTypes = {
onShuffle: React.PropTypes.func
}

Access child component state in other ways than ref?

I am unable to use something like this.refs.child.state in my application to access state of a child component, hence need an alternative way to do so. Main reason for this is to pass child contents to redux state when a certain button is clicked inside such childs parent component, hence function in parent component needs to pass childs content as one of the parameters.
Depending on the structure of your components (hard to tell when you don't post code), you could fix this just by chaining callbacks via props. I.e.
var Parent = React.createClass({
onChange: function(childValue){
this.setState({childValue: childValue});
},
render: function(){
return <Child onChange={this.onChange} />
}
});
var Child = React.createClass({
handleChange: function(event){
this.props.onChange(event.target.value);
},
render: function(){
return <input onChange={this.handleChange}/>
}
});
Add in as many middle-layers as needed of the form;
var MiddleChildA = React.createClass({
render: function(){
return <MiddleChildB onChange={this.props.onChange} />
}
});

How to pass state with parent to child component

Is there any way passing state from parent component to child component like:
var ParentComponent = React.createClass({
getInitialState: function() {
return {
minPrice: 0
}
},
render: function() {
return (
<div onClick={this.doSomething.bind(this, 5)}></div>
);
}
});
var ChildComponent = React.createClass({
getInitialState: function() {
return {
minPrice: // Get from parent state
}
},
doSomething: function(v) {
this.setState({minPrice: v});
},
render: function() {
return (
<div></div>
);
}
});
I want to change parent state value from child component. In react.js is it possible or not?
There is but it's not intended to work like that in React.
2-way data binding isn't the way to go in React, excerpt from the docs.
In React, data flows one way: from owner to child.
So what you want to do if you want to manipulate parent state in your child component is passing a listener.
//parent component's render function
return (
<Child listenerFromParent={this.doSomething} />
)
//child component's render function
return (
<div onClick={this.props.listenerFromParent}></div>
)
You can use the limelights solution, ie passing a function from the parent to the child.
Or you can also use projects like React-Cursor which permits to easily manipulate state passed from a parent component in a child.
I have made my home made framework (Atom-React, some details here) that also use cursors (inspired by Om), and you can somehow achieve easily 2-way data binding with cursors permitting to manipulate the state managed by a parent component.
Here's an exemple usage:
<input type="text" valueLink={this.linkCursor(this.props.inputTextCursor)}/>
The inputTextCursor is a cursor passed from a parent to a child component, and thus the child can easily change the data of the parent seemlessly.
I don't know if other cursor-based React wrappers use this kind of trick but the linkCursor function is implemented very easily with a simple mixin:
var ReactLink = require("react/lib/ReactLink");
var WithCursorLinkingMixin = {
linkCursor: function(cursor) {
return new ReactLink(
cursor.getOrElse(""),
function setCursorNewValue(value) {
cursor.set(value);
}
);
}
};
exports.WithCursorLinkingMixin = WithCursorLinkingMixin;
So you can easily port this behavior to React-Cursor

Categories

Resources