I'm with some questions on the use of component react.
Basically I want to use the "Title" component in various parts of the code, but it always has "states" that vary. I do not quite understand the official documentation on this issue, as I do to inherit this component, and only change the "states" for what I want?
I know the question seems silly, but I'm learning and React is very different from everything I saw.
var Title = React.createClass({
displayName: "Title",
getDefaultProps: function () {
return {
className: ""
}
},
render: function () {
return <h1 className={this.props.className}>{this.state.content}</h1>
}
});
You should use props rather than state in this instance. Props are owned by parents, and state is owned by the component itself. Since you would like to use this component in multiple places, then naturally a parent will decide what the text of the Title should be - therefore props are what you want.
React has a special prop children, which takes on the value of whatever is passed inside a component's JSX tag.
For instance, here's a mock component which uses your Title component multiple times:
var MyComponent = React.createClass({
render: function() {
return (
<div>
<Title className="foo">Hello</Title>
<Title className="bar">World</Title>
</div>
);
}
});
Since you are now passing text to the component as the children prop, you must update you Title component to render this:
var Title = React.createClass({
displayName: "Title",
getDefaultProps: function () {
return {
className: ""
}
},
render: function () {
// NOTE: we are now using children prop
return <h1 className={this.props.className}>{this.props.children}</h1>
}
});
A side benefit of this is that you can build more complex titles, containing multiple children, and it will just work:
var MyOtherComponent = React.createClass({
render: function() {
return (
<div>
<Title className="foo">
<span>Hello</span>
World
</Title>
</div>
);
}
});
You might want to replace {this.state.content} by {this.props.children} and use your component like this :
<Title className="myclass">my title</Title>
As a general rule, try to avoid using state when possible, dumb component are more easily reusable.
Related
I am working on a reactjs application - and I am breaking up a big component to have a child component. I've created a callback function in the child that will go back to the parent. When a checkbox is checked -- the child component does the callback and this goes back into the parent shell -- however I want to now jump out of the event callback and push the data to an original parent function.
my application kind of looks like this on a streamlined level.
var Parent = React.createClass({
onSelect: function(value, flag){
this.updateSelected(value, flag);
}
updateSelected: function(value, flag) {
let array = this.state.selectedArray;
array.push({"value": value, "flag": flag});
this.setState({
selectedArray: array
});
},
render: function() {
return (
<div>
<Child onSelect={this.changeHandler} />
<span>{this.state.value}</span>
</div>
);
}
});
var Child = React.createClass({
selectHandler: function(e) {
this.props.onSelect(e.target.value, false);
},
render: function() {
return (
<input type="checkbox" onSelect={this.selectHandler} />
);
}
});
but I can not just invoke the "this.updateSelected(value, flag);" as its inside the this scope.
You should use ES6 syntax and JSX syntax instead of pure React API, that help you a lot to reduce this kind of error about scoping and become your code more readable, but If you even want to use that syntax, so you should bind the function once you pass down to the child component , you can do it like this:
var Parent = React.createClass({
onSelect: function(value, flag){
this.updateSelected(value, flag);
}
updateSelected: function(value, flag) {
let array = this.state.selectedArray;
array.push({"value": value, "flag": flag});
this.setState({
selectedArray: array
});
},
render: function() {
return (
<div>
<Child onSelect={this.changeHandler.bind(this)} />
<span>{this.state.value}</span>
</div>
);
}
});
as you can see adding the .bind method and passing as argument the context that you want your function execute, then once your function be invoked, the scope of that function will be the parent component instead of child component.
Remember that this approach bind method could affect the improve of your component if your Parent component rerenders many times.
If you are using JSX syntax you should do something like
class YourComponent extends React.Component {
changeHandler = (value, flag) => {
this.updateSelected(value, flag);
}
render() {
return (
<div>
...
<Child onSelect={this.changeHandler} />
</div>
);
}
}
I want to call a function of a child component.
Is there a possibility to get refs from this.props.children in React.
var ComponentSection = React.createClass({
componentDidMount: function() {
// How to access refs in this.props.children ?
this.refs.inner.refs.specificPanel.resize();
},
render: function () {
return (
<div className="component-section" ref="inner">
{this.props.children}
</div>
);
}
});
var Panel = React.createClass({
resize: function() {
console.log('Panel resizing');
},
render: function () {
return (
<div className="Panel">
Lorem ipsum dolor sit amet
</div>
);
}
});
var MainComponent = React.createClass({
render: function () {
return (
<ComponentSection>
<Panel ref="specificPanel"></Panel>
</ComponentSection>
);
}
});
ReactDOM.render(
<MainComponent></MainComponent>,
document.getElementById('container')
);
I made a little demo: https://jsfiddle.net/69z2wepo/26929/
Thanks in advance
Ref in React is a relationship between a component and its owner while what you want is a relationship between an element and its parent. A parent-child relationship is opaque, the parent only receives the child elements on render and never gets access to the resolved components.
In your case, the ref="specificPanel" establishes a link from the MainComponent to the Panel. If you want to call methods of Panel from ComponentSection, it should own the Panels instead of receiving them as children.
You could always get creative with React.Children.map and React.createElement (cloning the elements by hand and thus stealing them from the original owner), but this introduces some serious potential pitfalls. A better approach would likely be re-thinking the ownership structure of your component tree.
You are trying to set a ref on a <div> instead of a React component.
You could also refactor your code so that only <ComponentSection> needs to know about the <Panel> component, and render it in it's render function.
var ComponentSection = React.createClass({
componentDidMount: function() {
this.refs.inner.resize();
},
render: function() {
return (
<div className="component-section">
<Panel ref="inner"/>
</div>
);
}
});
var MainComponent = React.createClass({
render: function() {
return (
<ComponentSection />
);
}
});
Here is a working JSFiddlle.
I'm new to React so please have mercy.
I've also read all of the threads on this, React / JSX Dynamic Component Name and React/JSX dynamic component names in particular. The solutions did not work.
I'm using a tab style interface where a user selects a tab and the appropriate content loads. A parent component stores the tab's content state, passes the corresponding props to the content child. This child then loads the correct content component (as its own child).
var TabbedContent = React.createClass({
loadMenu: function() {
var menus=this.props.carDivState.vehicleDetailState;
for (key in menus) {
if (menus.hasOwnProperty(key)) {
if (menus[key]) {
var Component='TabbedContent'+key;
return <Component />;
}
}
}
},
render: function() {
return (
<div className="TabbedContent">
<div className="contentWrapper">
{this.loadMenu()}
</div>
</div>
)
}
});
loadMenu loops through the props until it finds a true prop. It then returns that key (for instance "Overview") and creates a variable (e.g. Component='TabbledContentOverview').
However, my code returns an HTML tag <tabbedcontentoverview></tabbedcontentoverview>
Question
How do I get React to return the React component instead of an HTML tag? I appear to be using the correct capitalized naming conventions. I've read the Facebook docs. I just don't get it.
https://github.com/vasanthk/react-bits/blob/master/patterns/30.component-switch.md
import HomePage from './HomePage.jsx';
import AboutPage from './AboutPage.jsx';
import UserPage from './UserPage.jsx';
import FourOhFourPage from './FourOhFourPage.jsx';
const PAGES = {
home: HomePage,
about: AboutPage,
user: UserPage
};
const Page = (props) => {
const Handler = PAGES[props.page] || FourOhFourPage;
return <Handler {...props} />
};
// The keys of the PAGES object can be used in the prop types to catch dev-time errors.
Page.propTypes = {
page: PropTypes.oneOf(Object.keys(PAGES)).isRequired
};
First, if you are using Bootstrap for your app, I'd suggest that you use react-bootstrap`s tab. If you are not, I would suggest that you at least take a look at the implementation of their TabPane and TabbedArea.
Here's an example of how it looks like in your app:
const tabbedAreaInstance = (
<TabbedArea defaultActiveKey={2}>
<TabPane eventKey={1} tab='Tab 1'>TabPane 1 content</TabPane>
<TabPane eventKey={2} tab='Tab 2'>TabPane 2 content</TabPane>
<TabPane eventKey={3} tab='Tab 3' disabled>TabPane 3 content</TabPane>
</TabbedArea>
);
React.render(tabbedAreaInstance, mountNode);
Now, back to your question, if you want to create a component by name, just call React.createElement from inside your loadMenu:
loadMenu: function() {
var menus=this.props.carDivState.vehicleDetailState;
for (key in menus) {
if (menus.hasOwnProperty(key)) {
if (menus[key]) {
return React.createElement('TabbedContent'+key);
}
}
}
}
You need to have a reference to an actual class in order to create an element from it (in JS or JSX).
Hold a map of keys to React classes (i.e tabbedChildren), and just create this element using the JS API:
var childComponent = tabbedChildren[key]
return React.createElement(childComponent)
https://facebook.github.io/react/docs/top-level-api.html
I am trying to build an app that uses drag-and-drop behaviour, and the component being dragged needs to be cloned elsewhere in the DOM. Since the component is already mounted, trying to mount it again causes the browser to hang.
Trying to use cloneWithProps results in a Cannot read property 'defaultProps' of undefined error.
Here's a testcase:
var TestCase = React.createClass({
getInitialState () {
return {
draggingItem: null
}
},
render () {
return <div>
<ExampleComponent onClick={this.setDraggingItem} />
{this.state.draggingItem}
</div>
},
setDraggingItem (component) {
// This gives `Cannot read property 'defaultProps' of undefined`
//React.addons.cloneWithProps(component)
// This crashes the browser
//this.setState({ draggingItem: component })
}
})
var ExampleComponent = React.createClass({
render () {
return <div onClick={this.handleOnClick}>Hello World</div>
},
handleOnClick (event) {
this.props.onClick(this)
}
})
React.render(<TestCase />, document.body)
Of course I could simply clone component.getDOMNode() in setDraggingItem, but it really seems like rendering the component or calling cloneWithProps should work?
The two things you need to create an element is: the component class (e.g. ExampleComponent) and its props. cloneWithProps is only to be used in render and only with an element coming from props which was created in another component's render. You shouldn't save elements, or pass them around other than to other components in render. Instead, you pass around objects (props) and component classes.
Since you need to know the props and component class to render it in the first place, you can handle all of this in TestCase.
var TestCase = React.createClass({
getInitialState () {
return {
draggingItem: null,
draggingItemProps: null
}
},
render () {
return <div>
<ExampleComponent onClick={this.setDraggingItem.bind(null,
/* the component class */ ExampleComponent,
/* the props to render it with */ null
)} />
{
this.state.draggingItem && React.createElement(
this.state.draggingItem,
this.state.draggingItemProps
)
}
</div>
},
setDraggingItem (component, props, event) {
this.setState({ draggingItem: component, draggingItemProps: props })
}
});
var ExampleComponent = React.createClass({
render () {
return <div onClick={this.handleOnClick}>Hello World</div>
},
// just defer the event
handleOnClick (event) {
this.props.onClick(event)
}
});
If you wish to make these valid outside this TestCase component, ensure there aren't any functions bound to TestCase in the props. Also ensure there's no children prop with react elements in it. If children are relevant, provide the {componentClass,props} structure needed to recreate them.
It's hard to tell what your actual requirements are, but hopefully this is enough to get you started.
You need be sure you're creating a new component with the same props, not mount the same one multiple times. First, setup a function that returns an instantiated components (easier to drop JSX here):
function getComponent(props) {
return ExampleComponent(props);
}
Then in your TestCase render:
return (<div>
{ getComponent({ onClick: this.setDraggingItem }) }
{ this.state.draggingItem }
</div>);
That will create the first component. Then to create a clone:
setDraggingItem(component) {
var clone = getComponent(component.props);
}
This deals with the cloning part. You still have some dragging and rendering to figure out.
I’m trying to set some inline styles to children in React. I have this that actually works, sort of:
var App = React.createClass({
render: function() {
var children = React.Children.map(this.props.children, function(child, i) {
child.props.style.width = '100px'
})
return (
<div>{children}</div>
)
}
})
BUT, if I try to change styles when state changes, the styles will remain the same. You can easily see this when trying something like this: (fiddle: http://jsfiddle.net/9UvWL/)
var World = React.createClass({
getInitialState: function() {
return { width: 0 }
},
componentDidMount: function() {
this.setState({ width: 100 })
},
componentDidUpdate: function() {
console.log(this.getDOMNode().innerHTML);
},
render: function() {
var width
var children = React.Children.map(this.props.children, function(child, i) {
width = i*this.state.width
console.log('Setting width: '+width);
child.props.style = {width: (i*this.state.width)+'px'}
return child
}, this)
return <div>{children}</div>
}
})
var Hello = React.createClass({
render: function() {
return (
<World>
<div>1</div>
<div>1</div>
</World>
)
}
})
React.renderComponent(<Hello />, document.body);
It will log that width has changed, but the props.style is not working as expected. And using child.setProps() will throw "Invariant Violation: replaceProps(...): Can only update a mounted component.". Is there another "react-way" of changing inline styles to children?
You are modifying the props object of each child directly, which bypasses parts of React's update mechanisms. We will be making this impossible soon.
In this case you want to use cloneWithProps inside your World component's render function.
var children = React.Children.map(this.props.children, function(child, i) {
width = i*this.state.width;
console.log('Setting width: '+width);
return React.addons.cloneWithProps(child, {style: {width: width + 'px'}})
}, this)
return <div>{children}</div>
cloneWithProps creates a new copy of the component and merges in additional props. You should use this pattern to ensure you're giving React all the hints it needs to know when to update.
Here's your example working: http://jsfiddle.net/zpao/Tc5Qd/
Just after the "Can only update a mounted component" check, there is another more descriptive error message:
invariant(
this._mountDepth === 0,
'replaceProps(...): You called `setProps` or `replaceProps` on a ' +
'component with a parent. This is an anti-pattern since props will ' +
'get reactively updated when rendered. Instead, change the owner\'s ' +
'`render` method to pass the correct value as props to the component ' +
'where it is created.'
);
The logic comes from a different perspective than updating DOM nodes. When a DOM node needs to be changed, the way to do that normally is to get a reference to the DOM node, then set its properties. With React, instead of updating particular nodes, the render methods just return new React components with the correct properties set, then the back-end code works out which DOM nodes to update.
When something is shown wrongly in normal DOM code, it is hard to know where the problem is, since any code can update the DOM nodes.
When the code is arranged into render methods like in React, the render methods always take in this.props and this.state and return entire, fully rendered, components. Then, if something isn't rendered properly, it is always possible to look in the render method to find the problem, since that is the only place where that render happens. (And this works whether it is the first render or a second render, so there doesn't need to be a distinction between rendering components the first time, and updating components.)
So, the way around the problem you're describing, is to move the logic about rendering the divs into the render method which creates the divs, rather than in the render method of a different component. Something like this:
var World = React.createClass({
render: function() {
return <div>{this.props.children}</div>;
}
});
var Hello = React.createClass({
getInitialState: function() {
return { width: 0 };
},
componentDidMount: function() {
this.setState({ width: 100 });
},
render: function() {
var children = ["a", "b", "c"].map(function(content, i) {
return (
<div style={{ width: i * this.state.width }}>
{content}
</div>
);
}, this);
return (
<World>
{children}
</World>
)
}
});
React.renderComponent(<Hello />, document.body);
http://jsfiddle.net/kN4DV/2/