What's an alternative for the deprecated setProps() in React.js? - javascript

I just upgraded my project's React version to 13.3 and setProps() is no longer working. I'm using it in this Mocha test and I'm not sure how to rewrite it now. What are my options?
it('reloads search results when props change ', function() {
var loadResultsSpy = sinon.spy(searchView, 'loadSearchResults');
var newProps = {searchBy: 'foo', searchTerm: 'bar'};
searchView.setProps(newProps);
expect(loadResultsSpy.calledWith(newProps.searchBy, newProps.searchTerm)).toBe(true);
});

In the most general case, React recommends just re-rending the top-level component, which essentially just updates the props if the new render is of the same component type. Since TestUtils.renderIntoDocument is simply a shortcut for creating a DOM node and doing a regular render into it, something like this will work (I'm making some assumptions about the setup of your tests):
var searchView, div;
beforeEach(function() {
var comp = <SearchView searchBy="baz" searchTerm="quix" />;
div = document.createElement('div');
searchView = React.render(comp, div);
});
it('reloads search results when props change', function() {
var loadResultsSpy = sinon.spy(searchView, 'loadSearchResults');
var comp = <SearchView searchBy="foo" searchTerm="bar" />;
React.render(comp, div);
expect(loadResultsSpy.calledWith("foo", "bar")).toBe(true);
});

Related

Objects duplicating every component mount. How can I make it run only once ? In react

My object is a independent js file that I created.
componentDidMount() {
const node = ReactDOM.findDOMNode(this);
const widgetBuild = new window.WidgetFormBuilder({
form: $(node).parents('#dynamic_form_wrapper')
});
widgetBuild.initForm();
}
It is a little hard to workout without knowing more about WidgetFormBuilder.
However as good practice I would suggest...
componentDidMount() {
const node = ReactDOM.findDOMNode(this);
// Assign to the class instance
this.widgetBuild = new window.WidgetFormBuilder({
form: $(node).parents('#dynamic_form_wrapper')
});
this.widgetBuild.initForm();
}
componentWillUnmount() {
// Cleanup
// Check if WidgetFormBuilder has a destroy method or something similar.
// See https://reactjs.org/docs/react-component.html#componentwillunmount
this.widgetBuild = null;
}
shouldComponentUpdate() {
// Stop further re-renders, given you're using the DOM directly this could help prevent a few performance issues
// See https://reactjs.org/docs/react-component.html#shouldcomponentupdate
return false;
}
Finally, take a look at the react docs on third party libs.
I already fixed i just added a .destroy() function in my WidgetFormBuilder. :)
WidgetFormBuilder.prototype.destroyBuilder = function () {
const self = this;
const destroyEvents = function () {
$(self.form).unbind();
};
destroyEvents();
return this;
};

What is the purpose of skin-deep?

Let me start by saying that I am new to React and even newer to TDD coding in JavaScript. I am currently working on a React / Redux application where we are testing all of our components. Our current test suite consists of Jasmine, Mocha, Chai, Karma, and skin-deep. For some of our components we are using skin-deep (see below -- skin deep is )
import sd from 'skin-deep';
describe('testing-text-editor', () => {
let tree;
let instance;
let vdom;
let field;
let fieldInput;
let callbackFunction;
beforeEach(() => {
field = 'description';
fieldInput = '<div>I am the description</div>';
callbackFunction = () => console.log('foo');
tree = sd.shallowRender(React.createElement(TextEditor, { id: field, initialValue: fieldInput, onSubmit: callbackFunction }));
instance = tree.getMountedInstance();
vdom = tree.getRenderOutput();
});
it('testing-render-description', function() {
// some tests
});
})
Whenever we use skin-deep we are using it on components where we also use shallowRender. I was wondering if anyone has a good understanding of what skin-deep is really doing? I've been searching all morning for a good description of its functionality and purpose but am yet to find a satisfying answer.
EDIT: Moved some logic to the beforeEach to be more descriptive with my problem
Thanks!

Can I use Backbone with React Native?

I have found several different examples of integrating React (View) with Backbone on the web, but not any to do the same with React Native. I was wondering if this was possible and also if there were any samples to play with.
This (https://github.com/magalhas/backbone-react-component) also seemed a good starting point, but since I don't have any knowledge, I am not sure if it can still be used in React Native.
Thanks.
You'll be able to use some parts of Backbone, yes.
Backbone's views operate on the DOM in the web browser, and since React Native doesn't have that, you'll be unable to use Backbone view as-is.
However, notice that Backbone.React.Component is described as "a mixin that glues Backbone models and collections into React components". Therefore it works as the data layer of your application, supplying data to your view.
This would be useful in theory, but in practice I've just tried it and it doesn't actually work! That said, I did managed to tweak the code of Backbone.React.Component to fix it though, and this demo works:
'use strict';
var React = require('react-native');
var Backbone = require('backbone');
var backboneMixin = require('backbone-react-component');
var {
AppRegistry,
StyleSheet,
Text,
View,
} = React;
var MyComponent = React.createClass({
mixins: [backboneMixin],
render: function () {
return <Text>{this.state.model.foo}</Text>
}
});
var model = new Backbone.Model({foo: 'bar'});
model.set('foo', 'Hello world!');
var ReactNativeBackbone = React.createClass({
render: function() {
return (
<View>
<MyComponent model={model} />
</View>
);
}
});
AppRegistry.registerComponent('ReactNativeBackbone', () => ReactNativeBackbone);
You will see the value of foo from the model shown on screen as "Hello world!". You will need to edit lib/component.js in Backbone.React.Component like this:
diff --git a/node_modules/backbone-react-component/lib/component.js b/fixed.js
index e58dd96..0ca09f7 100644
--- a/node_modules/backbone-react-component/lib/component.js
+++ b/fixed.js
## -32,7 +32,7 ##
if (typeof define === 'function' && define.amd) {
define(['react', 'backbone', 'underscore'], factory);
} else if (typeof module !== 'undefined' && module.exports) {
- module.exports = factory(require('react'), require('backbone'), require('underscore'));
+ module.exports = factory(require('React'), require('backbone'), require('underscore'));
} else {
factory(root.React, root.Backbone, root._);
}
## -70,11 +70,11 ##
},
// Sets `this.el` and `this.$el` when the component mounts.
componentDidMount: function () {
- this.setElement(React.findDOMNode(this));
+ //this.setElement(React.findDOMNode(this));
},
// Sets `this.el` and `this.$el` when the component updates.
componentDidUpdate: function () {
- this.setElement(React.findDOMNode(this));
+ //this.setElement(React.findDOMNode(this));
},
// When the component gets the initial state, instance a `Wrapper` to take
// care of models and collections binding with `this.state`.
I made two changes:
Require React instead of react
Remove references to findDOMNode
I haven't done any extensive testing but this should get you started. I've also opened an issue to try and get the problem addressed in the main Backbone.React.Component code base.
Check this out: react-native-backbone

React - getting a component from a DOM element for debugging

For the purposes of debugging in the console, is there any mechanism available in React to use a DOM element instance to get the backing React component?
This question has been asked previously in the context of using it in production code. However, my focus is on development builds for the purpose of debugging.
I'm familiar with the Chrome debugging extension for React, however this isn't available in all browsers. Combining the DOM explorer and console it is easy to use the '$0' shortcut to access information about the highlighted DOM element.
I would like to write code something like this in the debugging console:
getComponentFromElement($0).props
Even in a the React development build is there no mechanism to use maybe the element's ReactId to get at the component?
Here's the helper I use: (updated to work for React <16 and 16+)
function FindReact(dom, traverseUp = 0) {
const key = Object.keys(dom).find(key=>{
return key.startsWith("__reactFiber$") // react 17+
|| key.startsWith("__reactInternalInstance$"); // react <17
});
const domFiber = dom[key];
if (domFiber == null) return null;
// react <16
if (domFiber._currentElement) {
let compFiber = domFiber._currentElement._owner;
for (let i = 0; i < traverseUp; i++) {
compFiber = compFiber._currentElement._owner;
}
return compFiber._instance;
}
// react 16+
const GetCompFiber = fiber=>{
//return fiber._debugOwner; // this also works, but is __DEV__ only
let parentFiber = fiber.return;
while (typeof parentFiber.type == "string") {
parentFiber = parentFiber.return;
}
return parentFiber;
};
let compFiber = GetCompFiber(domFiber);
for (let i = 0; i < traverseUp; i++) {
compFiber = GetCompFiber(compFiber);
}
return compFiber.stateNode;
}
Usage:
const someElement = document.getElementById("someElement");
const myComp = FindReact(someElement);
myComp.setState({test1: test2});
Note: This version is longer than the other answers, because it contains code to traverse-up from the component directly wrapping the dom-node. (without this code, the FindReact function would fail for some common cases, as seen below)
Bypassing in-between components
Let's say the component you want to find (MyComp) looks like this:
class MyComp extends Component {
render() {
return (
<InBetweenComp>
<div id="target">Element actually rendered to dom-tree.</div>
</InBetweenComp>
);
}
}
In this case, calling FindReact(target) will (by default) return the InBetweenComp instance instead, since it's the first component ancestor of the dom-element.
To resolve this, increase the traverseUp argument until you find the component you wanted:
const target = document.getElementById("target");
const myComp = FindReact(target, 1); // provide traverse-up distance here
For more details on traversing the React component tree, see here.
Function components
Function components don't have "instances" in the same way classes do, so you can't just modify the FindReact function to return an object with forceUpdate, setState, etc. on it for function components.
That said, you can at least obtain the React-fiber node for that path, containing its props, state, and such. To do so, modify the last line of the FindReact function to just: return compFiber;
Here you go. This supports React 16+
window.findReactComponent = function(el) {
for (const key in el) {
if (key.startsWith('__reactInternalInstance$')) {
const fiberNode = el[key];
return fiberNode && fiberNode.return && fiberNode.return.stateNode;
}
}
return null;
};
I've just read through the docs, and afaik none of the externally-exposed APIs will let you directly go in and find a React component by ID. However, you can update your initial React.render() call and keep the return value somewhere, e.g.:
window.searchRoot = React.render(React.createElement......
You can then reference searchRoot, and look through that directly, or traverse it using the React.addons.TestUtils. e.g. this will give you all the components:
var componentsArray = React.addons.TestUtils.findAllInRenderedTree(window.searchRoot, function() { return true; });
There are several built-in methods for filtering this tree, or you can write your own function to only return components based on some check you write.
More about TestUtils here: https://facebook.github.io/react/docs/test-utils.html
i wrote this small hack to enable access any react component from its dom node
var ReactDOM = require('react-dom');
(function () {
var _render = ReactDOM.render;
ReactDOM.render = function () {
return arguments[1].react = _render.apply(this, arguments);
};
})();
then you can access any component directly using:
document.getElementById("lol").react
or using JQuery
$("#lol").get(0).react
In case someone is struggling like me to access React component/properties from a chrome extension, all of the above solutions are not going to work from chrome extension content-script. Rather, you'll have to inject a script tag and run your code from there. Here is complete explanation:
https://stackoverflow.com/a/9517879/2037323
Here is a small snippet i'm currently using.
It works with React 0.14.7.
Gist with the code
let searchRoot = ReactDom.render(ROOT, document.getElementById('main'));
var getComponent = (comp) => comp._renderedComponent ? getComponent(comp._renderedComponent) : comp;
var getComponentById = (id)=> {
var comp = searchRoot._reactInternalInstance;
var path = id.substr(1).split('.').map(a=> '.' + a);
if (comp._rootNodeID !== path.shift()) throw 'Unknown root';
while (path.length > 0) {
comp = getComponent(comp)._renderedChildren[path.shift()];
}
return comp._instance;
};
window.$r = (node)=> getComponentById(node.getAttribute('data-reactid'))
to run it, open Devtools, highlight an element you want to examine, and in the console type : $r($0)
I've adapted #Venryx's answer with a slightly adapted ES6 version that fit my needs. This helper function returns the current element instead of the _owner._instance property.
getReactDomComponent(dom) {
const internalInstance = dom[Object.keys(dom).find(key =>
key.startsWith('__reactInternalInstance$'))];
if (!internalInstance) return null;
return internalInstance._currentElement;
}
React 16+ version:
If you want the nearest React component instance that the selected DOM element belongs to, here's how you can find it (modified from #Guan-Gui's solution):
window.getComponentFromElement = function(el) {
for (const key in el) {
if (key.startsWith('__reactInternalInstance$')) {
const fiberNode = el[key];
return fiberNode && fiberNode._debugOwner && fiberNode._debugOwner.stateNode;
}
}
return null;
};
They trick here is to use the _debugOwner property, which is a reference to the FiberNode of the nearest component that the DOM element is part of.
Caveat: Only running in dev mode will the components have the _debugOwner property. This would not work in production mode.
Bonus
I created this handy snippet that you can run in your console so that you can click on any element and get the React component instance it belongs to.
document.addEventListener('click', function(event) {
const el = event.target;
for (const key in el) {
if (key.startsWith('__reactInternalInstance$')) {
const fiberNode = el[key];
const component = fiberNode && fiberNode._debugOwner;
if (component) {
console.log(component.type.displayName || component.type.name);
window.$r = component.stateNode;
}
return;
}
}
});
Install React devtools and use following, to access react element of corresponding dom node ($0).
for 0.14.8
var findReactNode = (node) =>Object.values(__REACT_DEVTOOLS_GLOBAL_HOOK__.helpers)[0]
.getReactElementFromNative(node)
._currentElement;
findReactNode($0);
Ofcourse, its a hack only..

How can I pass props/context to dynamic childrens in react?

I am using react, and I am trying to pass props/context to my dynamic childrens,
by dymamic childrens I mean childrens are render using
{this.props.children}
How can I pass to this children (In my code I know it's type) context/props?
In this jsbin there is an example that it dosen't work on dynamic childrens.
http://jsbin.com/puhilabike/1/edit?html,js,output
Though #WiredPrairie's answer is correct, the React.addons.cloneWithProps is deprecated as of React v0.13RC. The updated way to do this is to use React.cloneElement. An example:
renderedChildren = React.Children.map(this.props.children, function (child) {
return React.cloneElement(child, { parentValue: self.props.parentValue });
});
There's not a a great way to do this that is clear and passing all the properties of the parent isn't a great pattern and could lead to some very difficult to follow code if not done carefully (and with excellent documentation). If you have a subset of properties though, it's straightforward:
JsFiddle
Assuming you're using React with Addons, you can clone the children of a React component and set new property values on them. Here, the code just copies a property called parentValue into each child. It needs to create a clone of each element as the child element had already been created.
var Hello = React.createClass({
render: function() {
var self = this;
var renderedChildren = React.Children.map(this.props.children,
function(child) {
// create a copy that includes addtional property values
// as needed
return React.addons.cloneWithProps(child,
{ parentValue: self.props.parentValue } );
});
return (<div>
{ renderedChildren }
</div>)
;
}
});
var SimpleChild = React.createClass({
render: function() {
return <div>Simple { this.props.id }, from parent={ this.props.parentValue }</div>
}
});
React.render((<Hello parentValue="fromParent">
<SimpleChild id="1" />
<SimpleChild id="2" />
</Hello>), document.body);
Produces:
Simple 1, from parent=fromParent
Simple 2, from parent=fromParent
Spreading props on DOM elements
https://github.com/vasanthk/react-bits/blob/master/anti-patterns/07.spreading-props-dom.md
When we spread props we run into the risk of adding unknown HTML
attributes, which is a bad practice.
const Sample = () => (<Spread flag={true} domProps={{className: "content"}}/>);
const Spread = (props) => (<div {...props.domProps}>Test</div>);

Categories

Resources