React - Passing props from Array to Div - javascript

I'm developing a more complex example of passing props from a component to another. In this case, it's the content of an array(when I click on that content) to a <div>.
You can find the code here: https://codesandbox.io/s/509j5npkwx
(Please check the code in the link above)
TextBox <div> component:
export class TextBox extends React.Component {
constructor(props) {
super(props);
this.state = {
content: "Select A Node To See Its Data Structure Here..."
};
this.changeContent = this.changeContent.bind(this);
}
changeContent(newContent) {
this.setState({
content: newContent
});
}
render() {
return (
<div className="padd_top">
<div className="content_box">{this.state.content}</div>
</div>
);
}
}
export default TextBox;
FileTree component:
export class FileTree extends React.Component {
constructor(props) {
super(props)
this.state = {
activeNode: null
}
this.setActiveNode = this.setActiveNode.bind(this)
}
setActiveNode(name) {
this.setState({ activeNode: name })
}
render() {
return (
<div className="padd_top">{
renderTree(
this.props.root || root,
this.setActiveNode,
this.state.activeNode
)
}
<TextBox />
</div>
)
}
}
I'm trying to do something like this, for further understanding: http://alexcurtis.github.io/react-treebeard/
I understood how to prepare the <div> to receive new information, by substituting the "Select A Node To See Its Data Structure Here..." when I click one of the elements belonging to the file tree.
I also understood how to prepare the file tree to pass content <div>, but in this case, I'm confused about where and how should I apply to the right component.
I'm new to React JS. If you have any tips for me about this issue, I'm very grateful.
Thank you.
I changed a bit the structure of my project, and now I'm looking forward to put <TextBox> and <FileTree> side by side.
More specifically, like this:
export class App extends React.Component {
render() {
return (
<div>
<div className="col-md-12">
<SearchEngine />
</div>
<div className="col-md-6">
<FileTree />
</div>
<div className="col-md-6">
<TextBox content={this.props.activeNode} />
</div>
</div>
);
}
}
I tought it wouldn't be different to pass props to <App>, but again I might be missing something, because it's not passing properly. What is missing here?
Thanks again.

I'm not sure if I understood your question.
Here is my fork: https://codesandbox.io/s/50pv75q8ll
Basically, you pass the activeNode to < TextBox />. Look at line 126 of index.js.
And then, in text_box.js use componentWillReceiveProps() to set the TextBox state with the content prop. Line 18 of text_box.js.

Related

How to create a List/Detail View in React

I need to implement a kind of Master/Detail View for a Web Application in React. Since the app should be integrated into a CakePHP app I can't use React Router for handling the routes (since CakePHP would process them).
I have a List of Items and want to navigate through them, showing a Detail View. Items are nested, so there're SubItems to navigate to.
For now I got a ItemList Component, showing a list of Cards with a clickhandler. How can I change the View without changing the url?
ItemList Component looks like:
class ItemList extends React.Component {
constructor(props) {
super(props);
this.state = {
itemList: []
}
}
componentDidMount() {
fetchItems(...)
}
render() {
return(
<div>
{this.state.itemList.map(item => (
<Item key={item.id} item={item} />
))}
</div>
);
}
}
Item Component looks like:
class Item extends React.Component {
constructor(props) {
super(props);
this.state = {
item: props.item,
}
}
handleClick = () => {
// How to navigate to another Component?
}
render() {
return(
<div>
<div className="card my-2" onClick={this.handleClick}>
<div className="card-body">
<h5 className="card-title">{this.state.item.title}</h5>
<p className="card-text">{this.state.item.description}</p>
</div>
</div>
</div>
);
}
}
Thanks in advance!
You should have a parent component (let's say MainView) that has a state (let's say selectedItemId).
class MainView extends React.Component {
constructor(props) {
super(props);
this.state = {
selectedItemId: [null]
}
}
componentDidMount() {
}
render() {
return(
{!selectedItemId && (<ItemList />)}
{selectedItemId && (
<ItemDetail id={selectedItemId} />
)}
);
}
}
As you can see, it renders different components based on the selectedItemId state value.
Inside the ItemList handleClick you call the setState of the parent MainView to set the selected item ID.
So using conditional rendering inside the render() function of MainView you can render the ItemList when no item is selected and ItemDetail when you have selected one.
I'm not really used to ES6 syntax components so my code can be wrong somewhere, but you can get the message ;)

In React JavaScript, How to call a class inside another class?

I began to learn JavaScript and React these days, I tried to draw some grids in a website and met a problem like this:
Everything works fine when I code like this:
export default class PathfindingVisualizer extends Component {
constructor(props) {
super(props);
this.state = {
nodes: [],
};
}
componentDidMount() {
const nodes = getInitialGrid();
this.setState({ nodes });
}
render() {
const { nodes } = this.state;
console.log(nodes);
return (
<>
<p style={{ fontSize: 40 }}>Visualize Algorithms</p>
<br />
<br />
<div className="node-container">{nodes}</div> // HERE WORKS FINE
</>
);
}
}
And the website turned out to be like this which is fine:
But when I changed the code like this:
render() {
const { nodes } = this.state;
console.log(nodes);
return (
<>
<p style={{ fontSize: 40 }}>Visualize Algorithms</p>
<br />
<br />
<NodeContainer>{nodes}</NodeContainer> // HERE
</>
);
}
}
The grids just disappear, and nothing in <body>:
Could anybody help me? I can't figure out why this is happening.
Class NodeContainer and Node are like this:
export default class NodeContainer extends Component {
render() {
return <div className="node-container"></div>;
}
}
export default class Node extends Component {
render() {
return <div className="node-item"></div>;
}
}
Hey, thank you guys for the answers:) this is my first time to ask a question here. I solved the problem by adding {this.props.xxxxx} as you said and it works.
Corrected codes as following:
...
<br />
<br />
<NodeContainer nodes={nodes}></NodeContainer> // HERE
</>
...
the NodeContainer class:
export default class NodeContainer extends Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
return <div className="node-container">{this.props.nodes}</div>; //HERE
}
}
I didn't use 'this.props.children', but will check out later. I skipped the basic tutorial so I didn't understand how to pass params to class, I checked this video to help myself quickly understand this:
https://www.youtube.com/watch?v=ICmMVfKjEuo&list=PLN3n1USn4xlntqksY83W3997mmQPrUmqM&index=5&t=0s
For this you need to call children inprops
export default class NodeContainer extends Component {
render() {
return <div className="node-container">{this.props.children}</div>;
}
}
I don't see where the Node class is being referenced so I'm not sure that's relevant.
Your issue is that the your passing the nodes component to the NodeContainer component, but not rendering it in NodeContainer. You should look into how props are passed to components - they appear as this.props.children on the component. Your code should look like this.
export default class NodeContainer extends Component {
render() {
return <div className="node-container">{this.props.children}</div>;
}
}
If you're wondering how nodes appears as this.props.children, it's because of how React treats components. You can achieve the same thing by passing it into children explicitly as a prop.
Dude, in reactJS, there's should be data to be pass from your Parent element to your Children element.
In your case to be able to show the data you want,
you need to pass your state from the <PathFindingVisualizer /> to your <NodeContainer />, which you have done it by using node as a children between <NodeContainer /> tag. And you forget the second step,
You need to access the data you have passed inside <NodeContainer /> class you made. How? just access it by using this.props.children.
here's the example.
export default class NodeContainer extends Component {
render() {
return <div className="node-container">{this.props.children}</div>
}
}
Problem solved.
as a reference see this. https://learn.co/lessons/react-this-props-children

How to pass callback from one child component to another

I have a main component, App, which has two child components, Player, and VideoList, where Player is a wrapper around react-player, heavily based off of the react-player demo.
Player has a method renderLoadButton() which creates a button that loads a particular video when clicked. I would like to have several of these buttons inside of my VideoList component.
I am attempting to pass the renderLoadButton() function up into the parent component, and then down into the VideoList component where I can call it.
Here is the code for render() function of the parent component. Both my <Player/> and <VideoList/> components instantiated here.
I get the following error on the line mentioned in the comment.
TypeError: Cannot read property 'renderLoadButton' of undefined
render() {
const dragHandlers = {onStart: this.onStart, onStop: this.onStop};
const {deltaPosition, controlledPosition} = this.state;
return (
<div className="App">
<div className="fullscreen">
<Draggable handle="strong" bounds={'body'}{...dragHandlers}>
<div style={{position: 'absolute', bottom: '30%', right: '50%'}} className="video-box no-cursor">
<Player ref={instance=>{this.player = instance}} title="VIDEO" url='https://streamable.com/nfec3'/>
</div>
</Draggable>
<Draggable handle="strong" bounds={'body'}{...dragHandlers}>
<div>
{/*Error on the following line*/}
<VideoList callback = {(x,y)=> this.player.renderLoadButton(x,y)}/>
</div>
</Draggable>
</div>
<div className="App-footer">
<img src={vinyl} className="App-logo" alt="logo" />
<h1>Radio</h1>
</div>
</div>
);
}
As per the code you provided you are doing it right i have created similar working model as yours it is working fine:
https://codesandbox.io/s/6y5p9woqq3
You can add your code to sandbox so that we will able to figure out what is the problem.
Edit
The Problem with your code is not index.js but is in VideoList.js as per your minimal code
VideoList.js:
import React, { Component } from "react";
class VideoList extends Component {
render() {
console.log("dd");
return this.props.callback('www.something.com','BUTTON');
}
}
export default VideoList;
Here you are trying to return a prop which contains a function not the original jsx for better clarity try console logging like this
console.log("dd",this.props.callback)
which shows a object returning your this.player.renderLoadButton function. so when you are trying to return it which returns just a function which cannot be rendered it is causing errors.
So if you have to pass that function which returns jsx don't use ref.Create a new obj or instance of Player class and extract the function from it and then pass it as prop to the videoList and the call it in render return.
so your App component should look like:
class App extends Component {
render() {
const obj = new Player
const func = obj.renderLoadButton
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<Player title="VIDEO" url='https://streamable.com/nfec3'/>
<VideoList func={func} />
</div>
);
}
}
then your VideoList looks like:
class VideoList extends Component {
render() {
console.log("dd");
return (
<div>
{ this.props.func('www.something.com','BUTTON') }
</div>
)
}
}
export default VideoList;
here is working code :https://codesandbox.io/s/jpqnxwyyy
Edit 2:
i don't think it is possible that way. one thing you can do is use the same jsx every where and use the another function as props every where to call again. like this: https://codesandbox.io/s/7zwyl0yp3j
When use this.METHOD_NAME you must initial method!
constructor(props){
super(props);
this.renderLoadButton = this.renderLoadButton.bind(this);
}
renderLoadButton(x,y){
console.log(x,y); // for example
}
render(){
return(
...
<VideoList callback={this.renderLoadButton}/>
...
)
}
If you want to use static methods of other class, first import class then use static methods like this:
import Player from 'PLAYER_FILE_LOCATION';
.
.
.
.
.
render(){
return(
...
<VideoList callback={Player.renderLoadButton}/>
...
)
}

Cannot read property of undefined when using map to render components

I'm new to React and still pretty new to programming in general and I've been practicing a bit just to get the basics down.
I currently have this:
class App extends Component {
constructor(props){
super(props);
this.state={strings: [],
textAreas: [<TextareaComponent ref='index0' onInput={this.onInputHandler} />,
<TextareaComponent ref='index1' onInput={this.onInputHandler} />]};
this.onInputHandler = this.onInputHandler.bind(this);
}
onInputHandler(){
const stringsArray = this.refs['index1'].refs.content.value.split(/\r|\n/);
this.setState({strings: (stringsArray)});
}
render(){
console.log(this.state.textAreas);
const renderTextareas = this.state.textAreas.map((textArea, index) => {
return <div key={index}>{textArea}</div>
})
return(
<div>
{renderTextareas}
</div>
);
}
};
class TextareaComponent extends Component {
constructor(props){
super(props);
}
render(){
return(
<div>
<textarea ref='content' onChange={this.props.onInput}></textarea>
</div>
);
}
};
Right now, my plan is to add a button that would push a new TextareaComponent to the textAreas array to render an additional textarea to the DOM. Currently I've just set the 2nd textarea as the element that I'd like to get values from.
Everything appears on the screen, but my problem is that I get this error when I type on any of the textareas that were rendered: Uncaught TypeError: Cannot read property 'index1' of undefined.
I noticed that when I just directly place the Textarea component with a ref of 'index1' on the parent component, the console logs the values properly and it seems to be able to read the 'index1' property. I'm not really sure where I'm getting it wrong.
Thanks in advance
You need to bind the onInputHandler function while passing it as a prop to the
TextareaComponent component
class App extends Component {
constructor(props){
super(props);
this.state={strings: [],
textAreas: [<TextareaComponent ref='index0' onInput={this.onInputHandler.bind(this)} />,
<TextareaComponent ref='index1' onInput={this.onInputHandler.bind(this)} />]};
this.onInputHandler = this.onInputHandler.bind(this);
}
onInputHandler(){
const stringsArray = this.refs['index1'].refs.content.value.split(/\r|\n/);
this.setState({strings: (stringsArray)});
}
render(){
console.log(this.state.textAreas);
const renderTextareas = this.state.textAreas.map((textArea, index) => {
return <div key={index}>{textArea}</div>
})
return(
<div>
{renderTextareas}
</div>
);
}
};
class TextareaComponent extends Component {
constructor(props){
super(props);
}
render(){
return(
<div>
<textarea ref='content' onChange={this.props.onInput}></textarea>
</div>
);
}
};

Passing method to child Component

My React app has several similar custom buttons that perform different tasks. The UI is similar among all the buttons, what changes is the action each one must trigger and the title.
The working code for the parent Component containing the buttons is (similar) to the following:
class Page extends Component {
constructor(props) {
super(props);
}
action1(){
//... do stuff...
}
action2(){
//... do stuff...
}
render(){
return(
<div className="table-row">
<div className="table-cell">
<div className="button"
onClick={this.action1.bind(this)}>{"button1"}
</div>
</div>
<div className="table-cell">
<div className="button"
onClick={this.action2.bind(this)}>{"button2"}
</div>
</div>
</div>
);
}
}
Is it possible to pass a method to the child component the same way it is done for a variable value? I want to turn it into something like this:
class Page extends Component {
constructor(props) {
super(props);
}
action1(){
//... do stuff...
}
action2(){
//... do stuff...
}
render(){
return(
<div className="table-row">
<div className="table-cell">
<CustomButton action={this.action1.bind(this)} title={"button1"}/>
</div>
<div className="table-cell">
<CustomButton action={this.action2.bind(this)} title={"button2"}/>
</div>
</div>
);
}
}
class CustomButton extends Component{
constructor(props){
super(props);
}
render() {
return (
<div className="table-cell"><div className="button"
onClick= {this.props.action}>{this.props.title}
</div>
);
}
}
What would be the correct way to handle this situation and the theory behind it?
I'm using React with Meteor, in case it makes a difference.
You can pass props to components. Passed props can have any data type of javascript.
In your case, you want to pass an action props which has a function as the value. Then you access action props in your component and use it.
In short, there is no theory behind it. What you are doing is correct. This is how react handles passing data to other components. Note that this is not the only way to pass data to child components.

Categories