Passing another component to a React Component - reactjs

React newbie here.
I am trying to create a simple category tree which allows you to render a JSON structure as a tree. The role of this JSONView is to be aware of the JSON structure and split it down into hierarchical tree. And the nodes of the tree will be parsed according to a custom rendering logic, which can be injected into this panel.
This complies with Single Responsibility Principle in the sense that JSONView is responsible for parsing the data and render the view component at leaves. The view component takes care of the rendering logic. I was able to achieve this by making the view component as a child component of the JSONViewPanel here :
http://jsbin.com/tiwijilide/1/edit?js,output
But I would really like to achieve something like this :
let x = {
"category":["first","second","third","fourth"]
};
class JSONPanel extends React.Component{
constructor(props){
super(props);
}
render(){
let {data, ...x} = this.props;
let dataNodes = data.category.map((category) => {
x = Object.assign(x, {text:category});
return (
<li><Component {...x}/></li> //Component passed as props
)
});
return (
<ul>{dataNodes}</ul>
);
}
};
The intention is to pass in any subtype of React component to this JSONView.
Is it possible without making Node component children of sub component as its done in the JSBin link?

Yes it is possible: try it!
let x = {
"category":["first","second","third","fourth"]
};
class JSONPanel extends React.Component{
constructor(props){
super(props);
}
render(){
let {data, ...x} = this.props;
let Renderer = this.props.renderer.component;
let dataNodes = data.category.map((category,rank) => {
x = Object.assign(x, {text:category})
return (
<li key={rank}><Renderer {...this.props.renderer.props} {...x}/></li>
)
});
return (
<ul>{dataNodes}</ul>
);
}
};
class RenderNode extends React.Component{
render(){
return(
<span className={this.props.will}>{this.props.text}</span>
)
}
}
var renderer={
component:RenderNode,
props:{will:"color-text-green"}
}
React.render(<JSONPanel data={x} renderer={renderer}/>, document.getElementById("react-example"));
The component can be given as props and must be call through a string with a capital first letter.

Related

How can I pass state to grandparent so that uncle/aunt can use it?

I've been struggling for hours trying to get some code to work. I'm new with React, but I have spent a lot time looking for a solution to this as well, and updating this code as I understood with no success.
Basically my app is a component that splits into two components, with one of those splitting into 9 buttons. When I click one of those buttons, I want its uncle/aunt to recognize that, and use the id of the button that was pushed to create a message.
I figured I should be passing the button id up to the grandparent so that it can pass the id down to the uncle/aunt. But its the passing the id to the grandparent I'm struggling with.
This is the general set up below:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
"x" : " "
};
getX(x){
this.setState({"x": x})
}
render() {
return (
<div>
<A getX={this.getX}/>
<B x={this.state.x} />
</div>
)
}
}
const A = (props) => {
const getX = (x) => props.getX(x);
a = [];
for (let i=0; i<9; i++) {
a.push(<C id={i} getX={getX}/>);
return <div>{a}</div>
}
class C extends React.Component {
constructor(props) {
super(props);
this.state = {
"id" : props.id,
"getX" : (x) => props.getX(x)
}
this.handleMouseDown = this.handleMouseDown.bind(this);
}
handleMouseDown(e) {
this.state.getX(e.target.id);
}
render() {
<div />
}
}
class B extends React.Component {
constructor(props){
super(props);
this.state = {
"x" : props.x
}
}
render() {
return <div>{this.state.x}</div>
}
}
Firstly, the getX() method of the App component doesn't seem to be working how I expected it to. By that I mean, when I add getX("7"); to the render method of the App component, just before the return statement, the whole thing crashes. But if I replace this.setState({"x": x}) with this.state.x = x in the getX() method, then the state sucessfully passes down to the component B, which is something at least. But, I don't understand why.
Secondly, I can't work out how to modify the App component's state from within component A. The getX() method of the App component doesn't seem to be passed into component A as I expected. Again, if I insert getX("7"); before the return statement of component A, the whole thing crashes again. I expected the getX function of component A to be the same function as the getX method of the App component, and therefore update the state of the App component. But I've had no success with that at all. I've even tried inserting this.getX = this.getX.bind(this) into the constructor of the App component, but that didn't solve everything for me.
Lastly, as you can probably guess, I cant modify the App component's state from any of the C components.
Any ideas? I'm stumped.
I have modified your example so that it works. A few things:
Dont copy props to state, that is an antipattern and creates bugs (as you have seen). Dont copy the id or the function passed from component A to component C, or in component B. Just use the props values.
You had some syntax errors that I fixed.
You didnt return the array created in component A.
(This is my preference, but I will argue that you are setting a value, not getting, so i renamed getX to setX.)
There was nothing returned from component C. I was not sure what you was suppoosed to be returning from that component, so I just created a button with a click-handler.
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
x: '',
};
this.setX = this.setX.bind(this);
}
setX(x) {
this.setState({ x: x });
}
render() {
return (
<div>
<A setX={this.setX} />
<B x={this.state.x} />
</div>
);
}
}
const A = (props) => {
let a = [];
for (let i = 0; i < 9; i++) {
a.push(<C id={i} setX={props.setX} />);
}
return <div>{a}</div>;
};
class B extends React.Component {
render() {
return <div>{this.props.x}</div>;
}
}
class C extends React.Component {
constructor() {
super();
this.handleMouseDown = this.handleMouseDown.bind(this);
}
handleMouseDown() {
this.props.setX(this.props.id);
}
render() {
return <button onClick={this.handleMouseDown}>Click me</button>;
}
}

reactjs -- adding more to state when compositing a component

if I have a class
class Role extends Component {
constructor(props) {
super(props);
this.state = {
role: 'something',
value: 1
}
}
render() {
let roleStatus = [];
for (let key in this.state) {
roleStatus.push(<p key={key}>{key}: {this.state[key]}</p>)
}
return (
<div>
<div>
{roleStatus}
</div>
</div>
);
}
and then another class that uses composition (asrecommended by React doc)
class specialRole extends Component {
constructor(props) {
super(props);
// I want to add some other attributes to this.state
}
render() {
return <role />
}
}
}
I want to be able to add another attribute, let's say name, to this.state, but when I use setState to add it, the render doesn't reflect to it. I'm wondering if it's because setState is not synchronized function. If that's the case, how should I achieve what I want to do?
What you'll want to do is think of it like a parent/child composition.
The parent has the logic and passes it to the child, which then displays it.
In your case, the parent should be Role, and the child component be something that renders Role's states, for example: RoleStatus. In addition, you can have another component called SpecialRoleStatus. Note the capitalized component names, component names should be capitalized.
The composition would look something like this:
class Role extends React.Component {
constructor(props) {
super(props)
//lots of state, including special ones
}
render() {
return(
<div>
<RoleStatus normalState={this.state.normalState} />
<SpecialRoleStatus specialState={this.state.specialState} />
</div>
)
}
}
Also, setState() won't behave like you want it to because it does not set the state of any other component other than its own component.

Reactjs coding with parent and child component

I have 3 components, that is 2 child components and one parent component.
I wanted to pass the child component value to parent (only the values not the components enitirely,it should not visible in parent) and this values from parent to another child component.
Any suggestions or logic of how to proceed on this, since I don't have any guidance as of right now I had to ask here. Is the above problem possible.
The code is very complex, so I have not put here.
Thank you
When you say values, do you mean state, props, user input, something else?
If you mean state or props: React has a 1-way data flow, so the easiest way to accomplish this is to actually store the data at a higher level. Either store the data used by the child in the parent and pass it down to the children for consumption, or else use a store that both parent and children have access to. Either way, this will make it much easier for all components to access the data.
If you mean user input: one way you can accomplish this is to pass a callback from the parent component to the child as a prop, and then in the child call that callback when a user does something or changes some value. The callback function can make the data accessible to the parent on that user action, and then you can decide what to do with the data from there.
AksharaDL,
Child to Parent — Use a callback and states
Parent to Child — Use Prop
Also here is another article explaining it: https://medium.com/#ruthmpardee/passing-data-between-react-components-103ad82ebd17
here is the solution.
in parrent component you have a state. and have a setData method to update state. pass setData to ChildOne use props. and data to ChilTwo and use it
class StoreBanner extends React.Component {
constructor() {
this.state = {
data: 'whatever'
}
}
setData = (data) => {
this.setState({data})
}
render() {
return (
<div>
<ChildOne setData={this.setData}/>
<ChildTwo data={this.state.data}/>
</div>
)
}
}
and in ChildOne you can update the parrent state
class ChildOne extends React.Component {
setParentData = () => {
this.props.setData('another')
}
...
}
You can do it like this.
class ParentComp extends React.Component {
constructor() {
super();
this.state = {
theStateToPass: null
}
this.receiveDataFromChild = this.receiveDataFromChild.bind(this);
}
receiveDataFromChild(data) {
this.setState({
theStateToPass: data
})
}
render() {
return (
<div>
<FirstChild updateMe={this.receiveDataFromChild} />
<SecondChild stateFromFirstChild={this.state.theStateToPass} />
</div>
)
}
}
class FirstChild extends React.Component {
constructor(props) {
super(props);
this.callParentMethod = this.callParentMethod.bind(this);
}
callParentMethod(e) {
let someDataToSend = "ABC";
this.props.updateMe(someDataToSend)
}
render() {
return (
<div onClick={this.callParentMethod}>
</div>
)
}
}
class SecondChild extends React.Component {
render() {
return (
<div>
{this.props.stateFromFirstChild}
</div>
)
}
}
however it becomes complex and might lead to one's pulling their hair out. so i would suggest using redux , it keeps the flow simple , you have a reducer , actions and a container. everything goes with a flow and keeps it clean but it does comes with an extra overhead of more code as you will be creating container reducer and actions.

How to update a stream of React components?

I have a simple functional component
const Hello = (props) => <div>{props.name}</div>
How can I supply a stream of values to the props parameter and make the component update in reactive fashion, consuming the stream? So that every time the new value comes from the stream, I get the component updated with that value.
Basically I am looking for a way to manually re-render the component.
You can use two basic methods:
Component rerendering, which you mentioned
State system: https://facebook.github.io/react/docs/state-and-lifecycle.html
The second way could look like this:
const Hello = (props) => <div>{props.name}</div>
class Wrapper extends React.Component {
constructor(props) {
super(props);
this.state = {
name: ''
};
}
componentDidMount() {
source.on('event', name => {
this.setState({name});
});
}
render() {
return <Hello name=this.state.name/>;
}
}

React: To put simple logic in Container or Presentational component?

I have a container component which passes an array of objects down to a presentational component to output.
In the presentational component, I need to display the count of a number of these objects that meet certain criteria. Is it best practice to perform the count in the container component and pass it down to the presentational component or is it OK to do this count in the presentational component.
ie:
export class ResultsPage extends React.Component {
constructor(props){
super(props);
}
countSexyObjects(){
const matching = this.props.allObjects.filter((obj)=>{
return obj.sexy === true;
});
return matching.length
}
render(){
return (
<PresentationalComponent allObjects={this.props.allObjects}
numberOfSexyObjects={this.countSexyObjects()} />
);
}
}
let PresentationalComponent = (props) => {
return (
<div>
There are {props.numberOfSexyObjects} sexy objects
</div>
);
};
OR
export class ResultsPage extends React.Component {
constructor(props){
super(props);
}
render(){
return (
<PresentationalComponent allObjects={this.props.allObjects} />
);
}
}
let PresentationalComponent = (props) => {
const countSexyObjects = () => {
const matching = this.props.allObjects.filter((obj)=>{
return obj.sexy === true;
});
return matching.length
};
return (
<div>
There are {countSexyObjects()} sexy objects
</div>
);
};
Ideally state is considered an evil in React. I understand that React is built upon the concept of state but less state is more preferred, which means try to structure the code with mostly functions that are pure in nature.
IMHO in your first example is more correct. The ResultsPage is your Container Component(smart component) while the other is dumb. Dumb component doesn't manage state and just takes care of how the UI looks. You can put all the html, bootstrap logic in there.
The reason why this pattern is good is because lets say now you want to fetch the matching criteria from an XHR call, your code in the second case would be
export class ResultsPage extends React.Component {
constructor(props){
super(props);
}
getSexyMatcher() {
/* make ajax call here */
return results;
}
render(){
return (
<PresentationalComponent allObjects={this.props.allObjects} sexyMatcher={getSexyMatcher()}/>
);
}
}
let PresentationalComponent = (props) => {
const countSexyObjects = () => {
const matching = this.props.allObjects.filter((obj)=>{
return obj.sexy.match(props.sexyMatcher)
// return obj.sexy === true;
});
return matching.length
};
return (
<div>
There are {countSexyObjects()} sexy objects
</div>
);
};
Notice how you had to change two components for the same business logic? Much worse, what if someone else used that PresentationalComponent elsewhere in the codebase?
In the first case things are much simpler. Just have to add the ajax function in the smart component and pass down the results to the UI component.
I would use the first format for a few reasons:
The smart component should a better idea of what a "SexyObject" is. If its a field in the object, that's pretty simple and could be argued either way. If it relies on a web service or some more complex logic to determine if it is sexy or not, you would never want that in the presentational layer. Simple has a way of turning complex, so I'd use the structure that supports the complexity initially.
Testing the code will be simpler with the logic in the smart component. You can prime your component and then check the output variables from your fixed data set.
If the criteria for "SexyObject" can change by the component, you would retain the ability to reuse your presentational component if you kept the selection logic separate.
Just my $0.02

Resources