React: Append Components to the DOM - javascript

I am rendering components in React by mapping through a JSON object. I now want to append these components to the dom. How can I select each of these components and append them to the DOM?
All of the usual jQuery methods are not working.
{dataobj.items.map( (instance) => {
return (
<div key={instance.title} className="new">
<Event time={parseInt(instance.start_time)} title={instance.title} start_time={instance.start_time} location={instance.location} />
</div>
)
})}
import AltContainer from 'alt-container';
import React from 'react';
import { Link } from 'react-router';
import moment from 'moment';
import Event from './Event.jsx';
const classNames = require('classnames');
const ReactDOM = require('react-dom')
export default class Calendar extends React.Component {
constructor(props) {
super(props);
this.state = {
}
}
componentDidMount() {
ReactDOM.findDOMNode(this)
console.log(this);
$('#nine').append($('new'))
}
render() {
var timeStamp = function() {
var datafile = require("json!./data.json");
{datafile.items.map(function(instance) {
const timeElement= parseInt(instance.start_time);
console.log(timeElement);
return timeElement
})}
}
var dataobj = require("json!./data.json");
return (
<div className="calendar">
<div className="amContainer">
<div className="amSide">AM</div>
<div className="linesContainer">
<div className="hourBlock">
<div id="nine" className="time">
</div>
<div className="halfHour">
{moment().format('9:30')}
</div>
</div>
<div className="hourBlock">
<div className="time">
{moment().format('10:00')}
</div>
<div className="halfHour">
{moment().format('10:30')}
</div>
</div>
<div className="hourBlock">
<div className="time">
{moment().format('11:00')}
</div>
<div className="halfHour">
{moment().format('11:30')}
</div>
</div>
</div>
</div>
{dataobj.items.map( (instance) => {
return (
<div key={instance.title} className="new">
<Event time={parseInt(instance.start_time)} title={instance.title} start_time={instance.start_time} location={instance.location} />
</div>
)
})}
</div>
);
}
}

First, you need to adjust your render method. You need to map over the dataObject, set it to a variable, in this case I call it objects and then use that in the jsx you already have as {objects} as a child like so:
render() {
// your render logic
var objects = dataobj.items.map( (instance) => {
return (
<div key={instance.title} className="new">
<Event
time={parseInt(instance.start_time)}
title={instance.title}
start_time={instance.start_time}
location={instance.location}
/>
</div>
)
return (
<ExampleComponent>
// your main html should go here this is just a simple example
{objects}
<ExampleComponent/>
)
}

Related

How to call an action from a component without connecting it to the redux store?

I have a Cards component which takes in the props from the UserPostscomponent(which is connected to the store) and displays cards. Cards is not connected to the redux store and I want to dispatch an action in the handleDelete function. How can I do that?
import React, { Component } from "react"
class Cards extends Component {
handleDelete = (id) => {
}
render() {
const { title, description } = this.props.post
const { postId } = this.props.post._id
return (
<div className="card">
<div className="card-content">
<div className="media">
<div className="media-left">
<figure className="image is-48x48">
<img
src="https://bulma.io/images/placeholders/96x96.png"
alt="Placeholder image"
/>
</figure>
</div>
<div className="media-content" style={{border: "1px grey"}}>
<p className="title is-5">{title}</p>
<p className="content">{description}</p>
<button className="button is-success">Edit</button>
<button onClick={this.handleDelete(postId)} className="button is-success">Delete</button>
</div>
</div>
</div>
</div>
)
}
}
export default Cards
UserPosts component which passes the props
<div>
{userPosts &&
userPosts.map(post => {
return <Cards key={post._id} post={post} />
})}
</div>
```
You can use the global store and directly call dispatch method. Not recommended. Hard to maintain and debug.
import { createStore } from 'redux'
const store = createStore(todos, ['Use Redux'])
// Dont create new one, use the one created in root
function addTodo(text) {
return {
type: 'ADD_TODO',
text
}
}
store.dispatch(addTodo('Read the docs'))
store.dispatch(addTodo('Read about the middleware'))

How to pass Array of Objects from Parent Component to Child Component in react

I'm working on a react project.
I have an array of objects in Parent component now how to pass an array of objects from parent to child in react
Parent.Component
import React from 'react';
import './App.css';
import Child from './Child/Child';
function App() {
const students = [
{
name: "Mark",
age: 21
},
{
name: "Williams",
age: 24
}
]
return (
<div className='App'>
<Child studentsArrayOfObject = { students }></Child>
</div>
)
}
export default App
Child.Component
import React from "react";
import "./Child.css";
function Child(props) {
return (
<div className="container">
<div className="row">
<div className="col-12">
<div className="Child">
{props.studentsArrayOfObject.map(student => (
<li>{student}</li>
))}
</div>
</div>
</div>
</div>
);
}
export default Child;
you are not suppose to print object directly, get some property from the object, and display like below. ex:name.
{props.studentsArrayOfObject.map(student => <li>{student.name}</li>)}
Try to fetch the properties of the objects and render it in JSX.You are trying to render the whole array as a React child. This is not valid. You should iterate through the array and render each element. Change your Child component to the following
Example
import React from 'react';
import './Child.css';
function Child(props) {
return (
<div className='container'>
<div className='row'>
<div className='col-12'>
<div className='Child'>
{props.studentsArrayOfObject.map(student => (
<li key={student.name}>{student.name}</div>
<li key={student.age}>{student.age}</div>
))}
</div>
</div>
</div>
</div>
)
}
export default Child
You can not just render the student object, I changed it to student.name here.
<div className="Child">
{/* {props.studentsArrayOfObject.map(student => (
<li>{student}</li>
))} */}
{props.studentsArrayOfObject.map(student => (
<li>{student.name}</li>
))}
</div>
Here is an online demo: https://codesandbox.io/s/so-60612610-y9y5z

App crashed when I try to map through and render data in React

Please tell me what the problem is here, I get data on restapi and in mapStateToProps I filter the data on input into the field and everything is fine console.log (cardList) displays what it should but how do I add this map - cardList.cards.map to display data, the app crashes.
If I remove this map, then again the data will be in the store and the app runs correctly.
I do not understand how the presence of a cycle does not allow to receive data in the store. please tell me
import React, {Component} from 'react';
import { connect } from 'react-redux'
import { getCards, findTags } from '../../actions/SessionActions'
import './Home.css'
import Sort from "./Sort/Sort";
import Filter from "./Filter/Filter";
class Home extends Component {
state = {
sortSelect: ''
}
componentDidMount() {
this.props.onGetCards();
}
handleTableSort = (e) => {
this.setState({sortSelect : e.nativeEvent.target.selectedOptions[0].text});
}
findTag = (e) => {
this.props.onFindTags(e.target.value);
}
render(){
let cardList = this.props.cards;
// if(this.state.sortSelect === 'Likes'){
// cardList = this.props.cards.sort((a, b) => a.likes - b.likes);
// }
// else if(this.state.sortSelect === 'Comment'){
// cardList = this.props.cards.sort((a, b) => a.comments - b.comments);
// }
console.log(cardList);
return(
<div className={'card-list'}>
<div className={'card-list__controls'}>
<Filter findTag={this.findTag.bind(this)}/>
<Sort handleTableSort={this.handleTableSort.bind(this)}/>
</div>
<div className={'card-list__head'}>
<div className={'card-list__head_row'}>Image</div>
<div className={'card-list__head_row'}>Tags</div>
<div className={'card-list__head_row'}>Likes</div>
<div className={'card-list__head_row'}>Comments</div>
</div>
{
cardList.cards.map((card) =>
<div className={'card-list__item'}>
<div className={'card-list__item_row card-list__item--img'}>
<img src={card.webformatURL} alt=""/>
</div>
<div className={'card-list__item_row card-list__item--tags'}>
<div className={'tags-wrap'}>
{
card.tags.split(', ').map((tag) =>
<span>{tag}</span>
)
}
</div>
</div>
<div className={'card-list__item_row card-list__item--likes'}>
<span>{card.likes}</span>
</div>
<div className={'card-list__item_row card-list__item--comments'}>
<span>{card.comments}</span>
</div>
</div>
)
}
</div>
)
}
}
const mapStateToProps = (state) => ({
cards: state.cards.filter(cards => cards.tags.includes(state.filterCards)),
});
const mapDispatchToProps = dispatch => ({
onGetCards: () => {
dispatch(getCards())
},
onFindTags: (name) => {
dispatch(findTags(name))
}
});
export default connect(mapStateToProps, mapDispatchToProps)(Home);
//filterCards
import { FIND_TAGS } from '../actions/SessionActions.js'
const initialState = '';
export default function filterCards(state = initialState, action) {
switch (action.type) {
case FIND_TAGS:
return action.payload
default:
return state
}
}
Since you are receiving data from an asynchronous function you need to leverage a conditional to only render once the data has been received. You will not have any data on the initial render until the promise has been fulfilled, hence your program crashing.
There are a few ways to do this and you can read more about that here https://reactjs.org/docs/conditional-rendering.html. My preferred method in your case would be to use cardList.length &&.
I updated your code below to use this logic.
render(){
let cardList = this.props.cards;
return(
<div className={'card-list'}>
<div className={'card-list__controls'}>
<Filter findTag={this.findTag.bind(this)}/>
<Sort handleTableSort={this.handleTableSort.bind(this)}/>
</div>
<div className={'card-list__head'}>
<div className={'card-list__head_row'}>Image</div>
<div className={'card-list__head_row'}>Tags</div>
<div className={'card-list__head_row'}>Likes</div>
<div className={'card-list__head_row'}>Comments</div>
</div>
{
cardList.length && cardList.cards.map((card) =>
<div className={'card-list__item'}>
<div className={'card-list__item_row card-list__item--img'}>
<img src={card.webformatURL} alt=""/>
</div>
<div className={'card-list__item_row card-list__item--tags'}>
<div className={'tags-wrap'}>
{
card.tags.split(', ').map((tag) =>
<span>{tag}</span>
)
}
</div>
</div>
<div className={'card-list__item_row card-list__item--likes'}>
<span>{card.likes}</span>
</div>
<div className={'card-list__item_row card-list__item--comments'}>
<span>{card.comments}</span>
</div>
</div>
)
}
</div>
)
}
}

Moving data between react components

So I'm trying to break the component on my App.js into a smaller component, that being my Sidebar.js. I took a small section of the code and put it in its own Sidebar.js file but no matter what I've tried, I cant call my function getNotesRows() from App.js without it being unable to find it or this.states.notes being undefined.
I just want it to send the code back and forth. This is a demo app, so I know it's not the most practical.
import React, { Component } from "react";
import classNames from "classnames";
import logo from "./logo.svg";
import checkMark from "./check-mark.svg";
import "./App.css";
import Sidebar from "./components/Sidebar.js";
class App extends Component {
constructor(props) {
super(props);
this.state = {
notes: [],
currentNoteIndex: 0
};
this.markAsRead = this.markAsRead.bind(this);
this.selectNote = this.selectNote.bind(this);
console.log("Test started 2.25.19 19:23");
}
componentWillMount() {
fetch('/notes')
.then(response => response.json())
.then(
notes => {
this.setState({
notes: notes,
currentNoteIndex: 0
})
}
)
.catch(
error => {
console.log('Ooops!');
console.log(error);
}
);
}
markAsRead() {
this.setState(currentState => {
let marked = {
...currentState.notes[currentState.currentNoteIndex],
read: true
};
let notes = [...currentState.notes];
notes[currentState.currentNoteIndex] = marked;
return { ...currentState, notes };
});
}
selectNote(e) {
this.setState({ currentNoteIndex: parseInt(e.currentTarget.id, 10) });
}
getTotalUnread() {
let unreadArray = this.state.notes.filter(note => {
return note.read === false;
})
return unreadArray.length;
}
getNotesRows() {
return this.props.notes.map(note => (
<div
key={note.subject}
className={classNames("NotesSidebarItem", {
selected:
this.props.notes.indexOf(note) === this.props.currentNoteIndex
})}
onClick={this.selectNote}
id={this.props.notes.indexOf(note)}
>
<h4 className="NotesSidebarItem-title">{note.subject}</h4>
{note.read && <img alt="Check Mark" src={checkMark} />}
</div>
));
}
// TODO this component should be broken into separate components.
render() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Notes Viewer Test App</h1>
<div>
Unread:
<span className="App-title-unread-count">
{this.getTotalUnread()}
</span>
</div>
</header>
<div className="Container">
<Sidebar />
<section className="NoteDetails">
{this.state.notes.length > 0 && (
<h3 className="NoteDetails-title">
{this.state.notes[this.state.currentNoteIndex].subject}
</h3>
)}
{this.state.notes.length > 0 && (
<p className="NoteDetails-subject">
{this.state.notes[this.state.currentNoteIndex].body}
</p>
)}
{this.state.notes.length > 0 && (
<button onClick={this.markAsRead}>Mark as read</button>
)}
{this.state.notes.length <= 0 && (
<p>
No Notes!
</p>
)}
</section>
</div>
</div>
);
}
}
export default App;
Above is my App.js
and below is the Sidebar.js that I'm trying to create
import React, { Component } from "react";
import "../App.css";
import App from "../App.js";
class Sidebar extends React.Component{
constructor(props) {
super(props);
}
render(){
return (
<section className="NotesSidebar">
<h2 className="NotesSidebar-title">Available Notes:</h2>
<div className="NotesSidebar-list">{App.getNotesRows()}</div>
</section>
)}}
export default Sidebar;
You cannot access a method like that. You need to pass the method as a prop and use it in the child.
<Sidebar getNotesRows={this.getNotesRows} />
and in Sidebar use
<div className="NotesSidebar-list">{this.props.getNotesRows()}</div>
In your sidebar, you're trying to call getNotesRows() from App, but Sidebar doesn't need access to app (you shouldn't have to import App in Sidebar.js). Instead, you should pass the function from App to your Sidebar component, and reference it from Sidebar's props.
In App.js, you'll need to bind getNotesRows and pass it to sidebar.:
<Sidebar getNotesRows={ this.getNotesRows } />
Then in Sidebar.js, you'll need to reference getNotesRows in your render method:
render() {
const notes = this.props.getNotesRows();
return (
<section className="NotesSidebar">
<h2 className="NotesSidebar-title">Available Notes:</h2>
<div className="NotesSidebar-list">{ notes }</div>
</section>
);
}
It seems like the problem here is that you are trying to use a class function as a static property, to put it simply, you have not initialized the App class when you import it into your sidebar(?), thus no static function was found on your App class so you can call App.getNotesRows() maybe you should re-think your components and separate them in container-components using a Composition Based Programming approach instead of OO approach.

React access parent's dom node from child component

I have a grid component as follow:
import React, { Component } from 'react';
import Action from './action.jsx';
class Grid extends Component {
constructor(props) {
super(props);
this.maxN = 110;
this.tempArray = [];
this.question;
}
getRandomN() {
var randomN = Math.floor((Math.random() * this.maxN) + 1);
if(this.tempArray.indexOf(randomN) === -1) {
this.tempArray.push(randomN);
}
else {
randomN = this.getRandomN();
}
return randomN;
}
getRandomQuestion() {
this.question = this.props.current.data.questions[this.getRandomN()];
return this.question;
}
render() {
this.getRandomQuestion();
return (
<section className="game">
<div className="grid">
<div className="row">
<div ref="n1"></div>
<div ref="n2"></div>
<div ref="n3"></div>
<div ref="n4"></div>
<div ref="n5"></div>
<div ref="n6"></div>
</div>
<div className="row">
<div ref="n7"></div>
<div ref="n8"></div>
<div ref="n9"></div>
<div ref="n10"></div>
<div ref="n11"></div>
<div ref="n12"></div>
</div>
<div className="row">
<div ref="n13"></div>
<div ref="n14"></div>
<div ref="n15"></div>
<div ref="n16"></div>
<div ref="n17"></div>
<div ref="n18"></div>
</div>
<div className="row">
<div ref="n19"></div>
<div ref="n20"></div>
<div ref="n21"></div>
<div ref="n22"></div>
<div ref="n23"></div>
<div ref="n24"></div>
</div>
</div>
<Action question={this.question} getRandomQuestion={this.getRandomQuestion.bind(this)}/>
</section>
);
}
}
export default Grid;
inside the "Action" component, based on the correct or wrong answer coming from "getNewQuestion" I need to access a random grid element from the grid component. (any random going from "n1" to "n24" as assigned to each ref attribute)
import React, { Component } from 'react';
class Action extends Component {
constructor(props) {
super(props);
this.state = {
question: props.question
}
}
getNewQuestion(e) {
console.log(this.state.question.correct_option);
let answerId = "option_" + this.state.question.correct_option;
if(e.target.getAttribute('data-question') == answerId) {
this.setState({
question: this.props.getRandomQuestion()
});
}
else {
console.log('wrong');
React.findDOMNode(this.refs.n1).classList.add('fdsdfsdfsdfsdfsfsdf');
}
}
render() {
let state = this.state;
return(
<div className="action">
<div className="action-question">
<h3>{state.question.question}</h3>
</div>
<div className="action-answers">
<p data-question="option_1" onClick={this.getNewQuestion.bind(this)}>{state.question.option_1}</p>
<p data-question="option_2" onClick={this.getNewQuestion.bind(this)}>{state.question.option_2}</p>
<p data-question="option_3" onClick={this.getNewQuestion.bind(this)}>{state.question.option_3}</p>
<p data-question="option_4" onClick={this.getNewQuestion.bind(this)}>{state.question.option_4}</p>
</div>
</div>
);
}
}
export default Action;
inside the "if" statment of the "getNewQuestion" I would like to do something like:
n2.classList.addClass('hidden');
I can't figure out how to access a parent's dom node from the "Action" component
Does the child really need to access the parent DOM directly? Shouldn't the parent Component know how to present itself? If so, then you can use callbacks that you pass down to the children, so that the children have the possibility to notify the parent when it should change.
const Child = ({modifyParent}) => (
<div onClick={ modifyParent } >Click me!</div>
);
const Parent = () => {
const modifyMyOwnStyle = event => {
// here you have easy access
// if you want to style the parent.
alert('Modifying style, based on child call');
}
return (
<Child modifyParent={ modifyMyOwnStyle }/>
);
};
ReactDOM.render(<Parent />, document.getElementById('root'));
Runnable JSFiddle demo here
You can get the ref of a component and pass this to its children like so:
render() {
return (
<div ref={node => this.node = node}>
<SomeChild parent={this.node} />
</div>
)
}
read more about it here: https://facebook.github.io/react/docs/refs-and-the-dom.html
However I have to say that doing this is usually a bad idea, and I would reconsider if you really need to pass the node, or if there is another way around the problem.
EDIT: As jonahe's comment shows you can usually get around the problem by passing a callback to the child component that you can fire when something needs to happen in the parent component
Better than accessing parent's DOM node directly, you can use a callback prop that does it for you.
Something like:
class Grid extends Component {
constructor(props) {
super(props)
this.accessRandomElement = this.accessRandomElement.bind(this)
}
accessRandomElement() {
// do you thing
}
render() {
this.getRandomQuestion()
return (
<section className="game">
...
<Action
question={this.question}
onAccessYourRandomElement={this.accessRandomElement}
///
/>
</section>
)
}
}
and then from inside Action you call this.props.onAccessYourRandomElement()

Categories