I've create a component with React.js
var A = React.createClass({ abc: function () {} })
How can i access abc method? A.prototype.abc is undefined
All methods on React components should be considered private. There are very rare exceptions. The public api of the component is the props it takes.
I decided to store methods in plain js object and to add method toReact. Exmaple:
var Game = {
render: function () {
return (
<PlayersTable data={ this.props.data } />
)
},
toReact: function () {
return React.createClass(this)
}
}
Related
If I simply have:
const App = function() {
return (
<div>{this.renderList()}</div>
)
}
How do I define the renderList method?
I can't do const renderList = function() {} (nor with var or let). I can't do renderList() {}.
What's the right syntax?
I am hesitant to give a solution to this because inline Stateless Functions are not supposed to have methods. if you want a method you should use a Class and theres nothing wrong with using a class. Its all based on what you need to do. Stateless Functions are designed to be a super light weight way to render something that doesn't need methods, or a state or even a this context (in terms of a class).
you should be doing it like this.
class App extends Component {
constructor(){
super();
// note this is a Stateless Component because its a react class without a state defined.
}
renderList = () => {
return <span>Something Here</span>;
}
render() {
return <div>{this.renderList()}</div>
}
}
a HACK way that I wouldn't recommend (but does solve your question in the way you want it to) would be like this.
const App = () => {
let renderList = () => {
return <span>Something Here</span>
}
return <div>{renderList()}</div>
}
The reason why its generally a bad practice is because you are creating a function and all the memory allocation needed every render cycle. Subsequently, the internal diffing optimizations that react provides is generally useless if you do this because a new function gives a different signature than the prior render cycle. If this had a lot of children, they would all be forced to re-render!
Edit - React Version 16.8.0 +
You can use Hooks to do this. I would recommend using memo to memoize the function, this way you aren't creating it in memory each render cycle.
const RenderList = React.memo(props => (
<span>Something Here</span>
))
const App = function() {
const renderList = ()=> {
return "this variables"
}
return (
<div>{renderList()}</div>
)
}
You would want to do something like this
const App = function() {
return (
<div>{renderList()}</div>
)
}
function renderList(){
return "this variables"
}
Naturally this is a bad approach you its recommended that you pass in functions as props and stateless component are always dumb componets. Say if you are using redux for example you can have your component render like this
import {connect} from 'react-redux';
const App = (props) => {
return (
<div> {props.renderList} </div>
)
}
function renderList (){
return "your render logic"
}
export default connect(null, {renderList})(App)
Can you try something like
const App = () => {
return (
<div>{this.renderList()}</div>
)
}
App.renderList = () => {
return 'This is my list'
}
You can create render list function as standalone and use function parameter to pass props into function.
I understand the question is not clear, so the description will help.
So, I have some react components like,
var LandingPage = React.createClass({
render: function() {
return <div>
This is the landing page.
</div>
}
})
and another component like
var FirstContent = React.createClass({
render: function() {
return <div>
This is first content page
</div>
}
})
Now I have a controller where I decide which one I need to render by passing a value in props, something like this -
var Contoller = React.createClass({
render: function() {
var inside = "";
if (this.props.pageName == "LandingPage") {
inside = <LandingPage />;
} else if (this.props.pageName == "FirstContent") {
inside = <FirstContent />;
}
return <div>
{inside}
</div>;
}
})
Now instead, I want to do something like, use the this.props.pageName inside the tag directly, so that I don't have to write if else for every time ad some new alternate content. Should be something like this -
var Contoller = React.createClass({
render: function() {
return <div>
<"this.props.pageName" /> //comment - <LandingPage /> if this.props.pageName = "LandingPage"
</div>;
}
})
The map of pageName to actual Component has to exist somewhere, because other than the default HTML elements (like div) React needs the class object reference to render a component. A string wont do.
How you manage this map is up to you, but I've used an object below.
Further complicating this is the JSX compilation step, which doesn't work with dynamic content. You will have to use the actual JS calls in your Controller to get this working.
Here is a codepen demonstrating this.
class LandingPage extends React.Component {
render() {
return <div> This is the landing page. </div>;
}
}
class FirstContent extends React.Component {
render() {
return <div> This is the first content page. </div>;
}
}
const map = {
LandingPage: LandingPage,
FirstContent: FirstContent
};
class Controller extends React.Component {
render() {
return React.createElement(map[this.props.pageName]);
}
}
React.render(<Controller pageName={'LandingPage'} />, document.body);
All that being said, I think you are building a router. You can use react-router in memory mode to perform routing without using the URL. Rolling your own setup here may be more work than it's worth.
The map does exist in the example by Tyrsius: you can use window[this.props.pageName]. Though it's better not to expose your components to the window object. And it may not work at all if you're using CommonJS for your React components.
If you don't need to build a name of several parts, why don't you just pass the component itself instead of a string? Either as a property or, better, as a child:
class Controller extends React.Component {
render() {
return <div>{this.props.children}</div>;
}
}
React.render(<Controller><FirstContent/></Controller>, document.body);
I'm trying to test a React component that requires the react-router in separately from app.js.
I have a component that does a redirect using the mixin Router.Navigation like so:
var React = require('react'),
Router = require('react-router');
var Searchbar = React.createClass({
mixins : [Router.Navigation],
searchTerm: function(e) {
if (e.keyCode !== 13) {
return;
}
this.context.router.transitionTo('/someRoute/search?term=' + e.currentTarget.value)
},
render: function() {
return (
<div className="searchbar-container">
<input type="search" placeholder="Search..." onKeyDown={this.searchTerm} />
</div>
)
}
});
module.exports = Searchbar;
I tried to write a test for this but ran into a wall. Apart from the fact that I'm unable to test that transitionTo works as expected, I've also encountered this error message in my Jest tests:
Warning: Failed Context Types: Required context router was not specified in Searchbar.
Does anyone know how I can get rid of the warning and bonus question, how I can test that the transition works as expected?
I've done research into this and this conversation on Github here: https://github.com/rackt/react-router/issues/400 is the closest I've found to the problem. It looks like I need to export the router separately but that seems like a lot of overhead to just run component tests without the warning a la https://github.com/rackt/react-router/blob/master/docs/guides/testing.md
Is that really the way to go?
In version 0.13 of React Router, the mixins Navigation and State were deprecated. Instead, the methods they provide exist on the object this.context.router. The methods are no longer deprecated, but if you're using this.context.router explicitly you don't need the mixin (but you need to declare the contextTypes directly); or, you can use the mixin, but don't need to use this.context.router directly. The mixin methods will access it for you.
In either case, unless you render your component via React Router (via Router#run), the router object is not supplied to the context, and of course you cannot call the transition method. That's what the warning is telling you—your component expects the router to be passed to it, but it can't find it.
To test this in isolation (without creating a router object or running the component through Router#run), you could place a mocked router object on the component's context in the correct place, and test that you call transitionTo on it with the correct value.
Because the router relies heavily on the lesser known context feature of React you need to stub it like described here
var stubRouterContext = (Component, props, stubs) => {
return React.createClass({
childContextTypes: {
makePath: func,
makeHref: func,
transitionTo: func,
replaceWith: func,
goBack: func,
getCurrentPath: func,
getCurrentRoutes: func,
getCurrentPathname: func,
getCurrentParams: func,
getCurrentQuery: func,
isActive: func,
},
getChildContext () {
return Object.assign({
makePath () {},
makeHref () {},
transitionTo () {},
replaceWith () {},
goBack () {},
getCurrentPath () {},
getCurrentRoutes () {},
getCurrentPathname () {},
getCurrentParams () {},
getCurrentQuery () {},
isActive () {},
}, stubs);
},
render () {
return <Component {...props} />
}
});
};
And use like:
var stubRouterContext = require('./stubRouterContext');
var IndividualComponent = require('./IndividualComponent');
var Subject = stubRouterContext(IndividualComponent, {someProp: 'foo'});
React.render(<Subject/>, testElement);
Here is my Jest file for a complete answer to this question. BinaryMuse’s last paragraph got me on the right track but I find code examples always the most helpful, so here it is for future reference.
jest.dontMock('./searchbar');
describe('Searchbar', function() {
var React = require('react/addons'),
Searchbar = require('../../components/header/searchbar'),
TestUtils = React.addons.TestUtils;
describe('render', function() {
var searchbar;
beforeEach(function() {
Searchbar.contextTypes = {
router: function() {
return {
transitionTo: jest.genMockFunction()
};
}
};
searchbar = TestUtils.renderIntoDocument(
<Searchbar />
);
});
it('should render the searchbar input', function() {
var searchbarContainer = TestUtils.findRenderedDOMComponentWithClass(searchbar, 'searchbar-container');
expect(searchbarContainer).toBeDefined();
expect(searchbarContainer.props.children.type).toEqual('input');
});
});
});
Hope this helps someone else in the future.
My answer is not Jest-specific but it might help people coming across the same problem.
I created a class to wrap router context.
Then in your test just add
<ContextWrapper><YourComponent/></ContextWrapper>
It can be useful to wrap other things like ReactIntl.
Note that you will lose the possibility to use shallow rendering but that's already the case with ReactIntl.
Hope that helps someone.
ContextWrapper.js
import React from 'react';
export default React.createClass({
childContextTypes: {
router: React.PropTypes.object
},
getChildContext () {
return {
router: {}
};
},
render () {
return this.props.children;
}
});
I'm wanting to create a React component that does a security check and if that passes it'll render out the children of it, if it fails then it won't render anything.
I've scaffolded out a component like so:
var RolesRequired = React.createClass({
permitted: roles => ...,
render: function () {
if (!this.permitted(this.props.roles)) {
return null;
}
return this.props.children;
}
});
The usage I was planning would be like this:
<RolesRequired roles={['admin']}>
<h1>Welcome to the admin</h1>
<div>
Admin stuff here
</div>
</RolesRequired>
How would you return all the children from the RolesRequired component?
I came up with this solution:
var RolesRequired = React.createClass({
permitted: roles => ...,
render: function () {
if (!this.permitted(this.props.roles)) {
return null;
}
return <div>{this.props.children}</div>;
}
});
What I'm doing is wrapping the children being returned in a <div> but I'm having to add an unwanted/unneeded DOM element to achieve it.
I think higher order components (HOC) are also a good candidate for this. You can basically wrap any component in HOC that defines some behaviour and decides if it should render a wrappe.
Nicest way to do this would be if you're using a ES2015 transpiler with some ES2016 features enabled (namely decorators):
function withRoles(roles) {
return function(Component) {
return class ComponentWithRoles extends React.Component {
constructor(props) {
super(props)
// Not sure where the data to get your roles about current user?
// from but you could potentially to that here if I'm getting your point
// and also setup listeners
this.state = { currentUser: 'admin' }
}
validateRoles() {
// you have access to the ``roles`` variable in this scope
// you can use it to validate them.
return true;
}
render() {
if (this.validateRoles()) {
return <Component {...this.props} />;
)
} else {
return <div>Nope...</div>;
}
}
}
}
}
// You can then use this on any component as a decorator
#withRoles({ showOnlyFor: [ 'admin' ] })
class AdminOnlyComponent extends React.Component {
render() {
return <div> This is secert stuff </div>
}
}
I've used ES2016 features because I think it's nicer to get the point across but you can implement that with just a simple function wrapping, here's a gist by one of the React core members on the topic of HOC:
https://gist.github.com/sebmarkbage/ef0bf1f338a7182b6775
The Facebook ReactJS library has strict rules about which component methods can be overridden and how. Unless it's specifically allowed, we cannot redefine a method.
For my custom mixins how can I update the SpecPolicy if I have a method I want to allow to be overridden? Is this even possible?
This example is a bit contrived but it should get the point across. Say I have the mixin below which is trying to provide a default renderItem method, intended to be overridden if necessary. When I attempt to render the component <Hello ... /> I get an Invariant Violation error. You can find a jsfiddle here.
var MyMixin = {
render: function () {
return this.renderItem();
},
renderItem: function () {
return <div>Hello {this.props.name}</div>;
}
};
var Hello = React.createClass({
mixins: [MyMixin],
renderItem: function() {
return <div>Hey {this.props.name}</div>;
}
});
This isn't possible right now. It's likely that a future version of React will have mixins that take advantage of ES6 classes and will be a bit more flexible. See here for a proposal:
https://github.com/reactjs/react-future/blob/master/01%20-%20Core/02%20-%20Mixins.js
You could just use something like jQuery extend to extend the object that's passed to React.createClass, instead of using a mixin - this would allow you to still use Mixins when you want, and use this method when you need to (JS Fiddle):
/** #jsx React.DOM */
var MyMixin = {
render: function () {
return this.renderItem();
},
renderItem: function () {
return <div>Hello {this.props.name}</div>;
}
};
var Hello = React.createClass($.extend(MyMixin, {
renderItem: function() {
return <div>Hey {this.props.name}</div>;
}
}));
React.renderComponent(<Hello name="World" />, document.body);
Maybe you can do something like this if you still want a default rednerItem implementation:
var MyMixin = {
render: function () {
return this.renderItem();
},
renderItem: function () {
var customRender = this.customRenderItem;
return customRender != undefined ? customRender() : <div>Hello {this.props.name}</div>;
}
};
var Hello = React.createClass({
mixins: [MyMixin],
customRenderItem: function() {
return <div>Hey {this.props.name}</div>;
}
});
The mixin has access to the component properties and state. You can have multiple implementations in the mixin and use the implement you need based on the properties/state.
Only thing you want to make sure that the components using the mixin have these properties/state or have a default implementation.
In situation, when i need to provide some properties to mixin, that depends on my react class, i make mixin as a function with needed arguments, that return mixin object
//mixin.js
module.exports = function mixin(name){
return {
renderItem(){
return <span>name</span>
}
};
}
//react-class.js
var myMixin = require('mixin');
module.exports = React.createClass({
mixins:[myMixin('test')],
render(){
return this.renderItem();
}
});
//result
<span>test</test>