React-on-Rails, passing and formatting props? - javascript

I'm trying to pass a collection of events objects down to a react-big-calendar component. I'm working from an example, and I'm very close - but there is something I'm missing. It is easier if I explain further with code examples:
Here is where the props are being passed in from the rails view. #events, is an array of event objects. The events will need to be formatted so they can be displayed in big calendar at some point:
<%= react_component("CalendarApp",
props: #events,
prerender: false) %>
This component is being passed to ReactOnRails.register in the following snippet:
import React from 'react';
import Calendar from '../containers/Calendar';
export default (props) => (
<Calendar {...props} />
);
Here is where 'CalendarApp' is being made available in the rails views:
import ReactOnRails from 'react-on-rails';
import CalendarApp from './CalendarAppClient';
ReactOnRails.register({ CalendarApp });
Next, is the Calendar container/smart component. The console.log(this.props) returns the following in the browser console:
Question How do I pass props down so that I can say something like 'this.props.events' in the following 'Calendar' component, and iterate through the list? Formatting the events so they can be passed to the MyCalendar presentation component in the correct format?
import React, { PropTypes } from 'react';
import MyCalendar from '../components/bigCalendar';
import _ from 'lodash';
// Simple example of a React "smart" component
export default class Calendar extends React.Component {
componentDidMount(){
console.log("mounted");
console.log(this.props);
};
render() {
return (
<div>
<MyCalendar events={this.props}/>
</div>
);
}
}
client/app/bundles/HelloWorld/components/bigCalendar.jsx
Ultimately I want to pass #events down to this component, my instinct is to figure out how to format the events in the container component above and pass down properly formatted events to the MyCalendar presentation component. Presently, I am using test data..which is why you see import events from './events';
import React, { PropTypes } from 'react';
import BigCalendar from 'react-big-calendar';
import moment from 'moment';
import events from './events';
BigCalendar.setLocalizer(
BigCalendar.momentLocalizer(moment)
);
const MyCalendar = props => (
<div className="calendar-container">
<BigCalendar
events={events}
scrollToTime={new Date(1970, 1, 1, 6)}
defaultDate={new Date(2015, 3, 12)}
defaultView='month'
defaultDate={new Date(2015, 3, 12)}
/>
</div>
);
export default MyCalendar;

First you need to correct you .erb react_component props
<%= react_component("CalendarApp",
props: {events: #events},
prerender: false) %>
Next you want to pass your this.props as a stringified object from the constructor
constructor(props) {
super(props);
this.state= {
examples: JSON.stringify(this.props.examples)
};
}
Something to that sort if you have an array of events using ES6 syntax you could do loop through this.state
var examples = this.state
examples = this.props.examples.map((example) => {
return(
<div key={example.id}>{example.name}</div>
);
});
Then you'll need to pass your newly created loop where ever you want it
return(
<div class="col-md-10">
{examples}
</div>
);
and bam! you've got it! just remember to pass the data from your .erb controller

Related

GooglePlacesAutocomplete - passing info to parent component and defining language

In my react sign-up form I have the GooglePlacesAutocomplete component to autocomplete user city and country. I have a question - how do I pass the selected city and country to my parent component's state? Normally I use onChange but it doesn't work. onPress doesn't work either (or I am using it incorrectly).
The code looks something like this:
import React, { Component } from "react";
import GooglePlacesAutocomplete from 'react-google-places-autocomplete';
export default class SignUp extends Component {
constructor() {
super();
this.state = {
cityAndCountry: ''
};
}
render() {
return (
<h2>Place of birth</h2>
<GooglePlacesAutocomplete
apiKey="myApiKey"
autocompletionRequest={{ types: ['(cities)'] }}
apiOptions={{ language: 'en'}}
)
}
}
You can use this prop defined in their official docs.
<GooglePlacesAutocomplete
selectProps={{
value,
onChange: setValue,
}}
/>

Functional component to class component Error: Cannot read property 'id' of undefined

I'm working on a small project and am in the process of translating a functional component to a class based component so that my component can manage its state. In translating the component over, I've run into an error:
TypeError:Cannot read property 'id' of undefined
This component worked as a functional component so I'm not sure why I'm receiving this error now. Can anyone answer why in translating from a functional component to a class based one that I'm receiving this error? Is this something to do with scope at all? Do I just have some syntax wrong? I've been banging my head against this for a while and just can't understand why it cannot read the property of 'id' which is in now in the return of the render portion of my component.
Component code:
import React, {Component} from "react";
import "./OpenTasksComponent.css"
class openTasks extends Component{
constructor(){
super();
this.handleClick = this.handleClick.bind(this);
}
handleClick(){
let x = document.body.nodeName;
console.log(x);
}
render(){
return (
<div className="tasks" value = {this.task.id}>
<h1 onClick={this.handleClick}>{this.task.clientName}</h1>
<div className="accordion-item accordion-item-open">
<h2>{this.task.matter}</h2>
<p>{this.task.matterStatus}</p>
</div>
</div>
)
}
}
export default openTasks
App code:
import './App.css';
import Task from './components/open-tasks-component/open-tasks-component';
import tasksData from './components/open-tasks-component/tasks-data';
import Header from './Header';
function App() {
const Tasks = tasksData.map(task=> <Task key={task.id} task ={task}/>)
return (
<div className="App">
<Header />
{Tasks}
</div>
);
}
export default App;
In App you passed a prop down, but you tried to read it in the child class component using this.task.id it should be this.props.task.id
Functional components can also manage states.
You can use useState.
const [state, setState] = useState(null);

How to pass down data through 3 class components in react correctly?

This is a react beginners exercise, so please be patient and don't overwhelm with the latest best practice.
For this exercise I need 3 class components:
Main.js: which basically just holds an array
List.js: which maps through that array and with each iteration passes the element of the array to Item.js
Item.js: here you render the mapped array
I am quite confident with the first two components but unable to make the jump from 2 to 3. Who can not only post the answer but also comment out the key ideas? Thx!
What I have so far for Main.js and List.js
Main.js
import React from "react";
export const avengers = ["Ironman", "Thor", "Hulk", "Hawkeye", "Black Widow"]
List.js
import React from "react";
import Main, { avengers } from "Main.js";
class List extends React.Component {
render() {
return (
<div>
{avengers.map((superhero, index) =>
<Item key={index} superhero={superhero}/>
)}
</div>
);
}
}
export default List;
Item.js
import React from "react";
import ReactDOM from "react-dom";
import List from "./List.js";
class Item extends React.Component {
render() {
return (
<div>
{this.props.superhero}
</div>
);
}
}
export default Item;
One problem you have is when passing props
<Item key="index" superhero="{superhero}" />
You should be passing it like
<Item key={index} superhero={superhero} />
If you put is ass someProp="something" you are just passing a string.
If you want to pass a variable, you need to pass it like someProp={someVariable}
And the prop can be access inside Item like this.props.someProp (if it's a class component, or props.someProp if is a functional component, where props is the parameter of your functional component.
Edit
One mistake I just saw in your code is these two lines
const avengers = ["Ironman", "Thor", "Hulk", "Hawkeye", "Black Widow"]
and
import avengers from "Main.js";
In the first line, you create avangers but you never export it, so you want be able to import it.
What you need to do is add export and when importing, import it by the name (inside {}) because the way you are importing, you are getting the default export.
So here is how it should be
// added export
export const avengers = ["Ironman", "Thor", "Hulk", "Hawkeye", "Black Widow"]
and
// importing avengers correctly
import Main, { avengers } from "Main.js";
Edit 2
Not sure if this is what you want, but I will guess
class Item extends React.Component {
render() {
return (
<div>
{this.props.superhero}
</div>
);
}
}
export default Item;
Last Edit
I made you a codesandbox with a working example. Please take a look and see how it works, play around with it and try to learn from it. Hope this helps ;)

How can pass a props to a variable?

I'm trying "hydrate" props from elements to child components that will render. The problem is that I can't figure out how I can do it with my configuration.
I have seen this answer, I tried to adapt it, but so far I'm getting errors (see bottom).
For a bit of background, I'm developing a Rails based application that uses React for the front end. So I don't use React router or such, it just "displays" the datas.
Here is how I set everything up:
front.js (where everything gets rendered)
import React from 'react';
import ReactDOM from 'react-dom';
import extractActionName from './lib/extractActionName';
import {elementForActionName} from './lib/elementForActionName';
import 'jquery';
import 'popper.js';
import 'bootstrap';
let actionName = extractActionName();
let value = "value";
let renderElement = function (Element, id) {
ReactDOM.render(
<Element value={value} />,
document.getElementById(id)
);
};
renderElement(elementForActionName[actionName], actionName);
lib/elementForActionName.js
import React from 'react';
import Homeindex from '../home/home';
import Contact from '../home/contact';
// This files create an associative array with id React will be
// looking for as a key and the component as value
export const elementForActionName = {
'index': <Homeindex />,
'contact': <Contact/>,
};
lib/extractActionName.js
export default function extractActionName() {
// The body contains classes such as "home index", so
// I return the "action name" of my controller (home) to
// front.js so I will render the good component
return document.body.className.split(' ').pop();
}
home/home.js
import React from 'react';
import Header from '../layout/header';
import Footer from '../layout/footer';
export default class homeIndex extends React.Component {
render() {
return(
<div>
<Header/>
<h1>Hello this will be the content of the landing page hello</h1>
<Footer/>
</div>
)
}
}
My problem is that I'd like to make an Ajax call in my "front.js" file, then transmit the received data (here, "value"). The error I'm getting is the following:
Uncaught Error: Element type is invalid: expected a string (for
built-in components) or a class/function (for composite components)
but got: object.
I'm lacking experience with React, how can I resolve this problem?
Thank you in advance.
You are currently returning the instance of a component:
export const elementForActionName = {
'index': <Homeindex />, <--- here
'contact': <Contact/>,
};
And then attempting to instantiate it again:
let renderElement = function (Element, id) {
ReactDOM.render(
<Element value={value} />, // <--- here
document.getElementById(id)
);
};
Instead, just use the component class:
export const elementForActionName = {
'index': Homeindex,
'contact': Contact,
};

How to have text dynamically change with React, but not call the change in Render?

I'm trying to build a very simple app with React that will tell you how many days have past since a given date. I am using React, Moments, and ReactJS Datepicker as the foundation of this app for quick implementation.
I'm getting an error when I attempt to pick a date saying
warning.js:44
Warning: setState(...): Cannot update during an existing state transition (such as within 'render' or another component's constructor)
I understand it means I can't have a function update a state I am currently displaying due to render issues, but am having a hard time figuring out how I can get such functionality without updating it in the render method.
This is the current app:
import React, { Component } from 'react';
import DatePicker from 'react-datepicker';
import moment from 'moment';
import './App.css';
import 'react-datepicker/dist/react-datepicker.css';
class App extends Component {
constructor() {
super();
this.handleChange = this.handleChange.bind(this)
this.state = {
startDate: moment(),
dateDiff: moment(),
dateCheck: false
};
}
handleChange(date) {
this.setState({
startDate: date,
dateCheck: true
});
}
showDiff(date, dateCheck) {
if (dateCheck) {
this.setState({
dateCheck: false,
dateDiff: moment().startOf(date).fromNow()
});
}
}
render() {
return (
<div className="App">
<div className="DatePicker">
<DatePicker
selected={this.state.startDate}
onChange={this.handleChange}
showYearDropdown
dateFormatCalendar="MMMM"
isClearable={true}
placeholderText='Enter A Date' />
</div>
<div>
{this.showDiff(this.state.startDate, this.state.dateCheck)}
</div>
</div>
);
}
}
export default App;
I'm trying to have it update the number of calculated days every time a new date is selected, and thought the dateCheck flag would help, but it didn't.
Thanks
React automatically re-renders parts of the DOM that need updating upon state change. The idea is to tell React what you want it to render, not how you want it to render.
So in your case, if you just want React to render the date difference from now, you need only change startDate when it updates. Here's some code to try to illustrate what I'm suggesting:
import React, { Component } from 'react';
import DatePicker from 'react-datepicker';
import moment from 'moment';
import './App.css';
import 'react-datepicker/dist/react-datepicker.css';
class App extends Component {
constructor() {
super();
this.handleChange = this.handleChange.bind(this)
this.state = {
startDate: moment()
};
}
handleChange(date) {
this.setState({
startDate: date
});
}
render() {
return (
<div className="App">
<div className="DatePicker">
<DatePicker
selected={this.state.startDate}
onChange={this.handleChange}
showYearDropdown
dateFormatCalendar="MMMM"
isClearable={true}
placeholderText='Enter A Date' />
</div>
<div>
{this.state.startDate.fromNow()}
</div>
</div>
);
}
}
export default App;

Categories

Resources