I have a list component that is getting information from a local json file if I do something like the following everything works as expected.
import React from 'react';
import caseStudies from './case-studies.json';
import CaseStudyItem from './case-study-list';
const CaseStudyList = () => {
const caseStudyItems = caseStudies.map( caseStudy => {
console.log(caseStudy);
return (
<div key={caseStudy.name}>{caseStudy.name}</div>
);
});
return (
<ul>
{caseStudyItems}
</ul>
);
}
export default CaseStudyList;
The expected number of case studies is printed out with no problem.
If however I replace the div inside of the caseStudies.map statement with the following
<CaseStudyItem key={caseStudy.id} caseStudy={caseStudy} />
I get a "Maximum Callstack size exceed error" if I add a log statement in there it shows it spitting out hundred and hundreds of items. What would cause this?
In case its needed here is the CaseStudyItem component, which is just blank now for testing purposes.
import React from 'react';
const CaseStudyItem = () => {
return (
<div>case Study</div>
);
}
export default CaseStudyItem;
I think you are making new CaseStudyLists inside each CaseStudyList. This is because you (probably) import from the wrong file:
import CaseStudyItem from './case-study-list';
should perhaps be
import CaseStudyItem from './case-study-item';
When you import like this, you can name the imported variable whatever you want, so what you though was the CaseStudyItem component creator was actually the CaseStudyList component creator.
Related
I have a rails (7.0.2) application and just installed React. I'm very new to react and can't seem to understand why it looks like my component is loading multiple times, the first time with an empty value for props and the second time with the correct values for props.
App.js:
import "./App.css";
import axios from "axios";
import Customers from "./components/customers";
import { useEffect, useState } from "react";
const API_URL = "http://localhost:3000/internal_api/v1/customers";
function getAPIData() {
return axios.get(API_URL).then((response) => response.data);
}
function App() {
const [customers, setCustomers] = useState([]);
useEffect(() => {
let mounted = true;
getAPIData().then((items) => {
if (mounted) {
setCustomers(items);
}
});
return () => (mounted = false);
}, []);
console.log('LOADED App.js');
return (
<div className="App">
<h1>Hello</h1>
<Customers customers={customers} />
</div>
);
}
export default App;
and customers.js:
import React from "react";
function Customers(props) {
console.log('LOADED customers.js');
return (
<div>
<h1>These customers are from the API</h1>
{props.customers.data.map((customer) => {
return (
<div key={customer.id}>
<h2>{customer.id}</h2>
</div>
);
})}
</div>
);
}
export default Customers;
When I remove this part of the code and reload the page, my props come through correctly when looking in console. Then, when I put the code back and save (without reloading), it displays correctly.
{props.customers.data.map((customer) => {
return (
<div key={customer.id}>
<h2>{customer.id}</h2>
</div>
);
However, as soon as I reload again, I get the same following error:
Uncaught TypeError: Cannot read properties of undefined (reading 'map')
It seems as though the first time everything renders, props is empty. Then the second time, it is full with the data. I checked my rails app and it only hits the API once. What am I doing wrong?
More log outputs:
React component rendering multiple times?
React will render fast before completing the request in use Effect
so in first render customers array will be empty
when request is fulfilled, you are changing state, So react will re-render the component
Only component that uses state reloads when the state is changed this is required else UI will not update
failing when reloading the page? | Failed on Initial Load
Since in Initial render customers will have no data customers.data will be undefined so it will not have map
to bypass this error use props.customers?.data && props.customers.data?.map() addding question mark means expression will be evaluated if not undefined
Source - Optional_chaining
I'm currently working on using React to upload a CSV file and convert the data to an array so I can access phone numbers. I've actually got it almost completely functional, with just one problem: I can't figure out how to store the array properly in a variable (dataDump) on the global level. It stores it inside another array.
Here's a picture of my console so you can see what I mean.
I'm able to access the contents of dataDump if I use dataDump[0] (as seen in the function for handleClick), but that won't work for a global variable. I need to be able to send the array's values to other components/files, so I don't think having to call it like that will work. Chances are I'm over-complicating this in my head and the answer is incredibly simple, but I've spent the past 2-3 weeks learning React, Twilio, Mongodb etc. from scratch so my brain's not cooperating.
I'll appreciate any help! Thanks! Code below. (Note this is a component that's imported to the App page.)
import React from "react";
import CSVReader from "react-csv-reader";
var dataDump = [];
console.log(dataDump);
const papaparseOptions = {
header: true,
dynamicTyping: true,
skipEmptyLines: true,
transformHeader: header => header.toLowerCase().replace(/\W/g, "_"),
complete: function(results) {
dataDump.push(results.data);
console.log(dataDump);
var rows = results.data;
let numbers = rows.map(a => a.phone_number); //make the results ONLY the phone numbers
// console.log(numbers);
document.getElementById("data2").innerHTML=numbers; //display the phone numbers
}
};
class Import extends React.Component {
constructor(props) {
super(props);
this.state = {data:[]};
this.handleClick = this.handleClick.bind(this);
}
handleForce = data => {
// console.log(data.length);
console.log(data);
this.setState({data: data});
};
handleClick = () => {
console.log("success");
console.log(this.state.data);
console.log("Next is Numbies:");
let numbies = dataDump[0].map(a => a.phone_number);
console.log(numbies);
document.getElementById("data").innerHTML=numbies;
}
render() {
return (
<div className="container">
<CSVReader
className="csv-input"
label="Select CSV file to import"
onFileLoaded={this.handleForce}
parserOptions={papaparseOptions}
/>
<div>
</div>
<button onClick={this.handleClick.bind(this)}>
Test
</button>
<div id="data" />
<div id="data2" />
<div id="data3">
</div>
</div>
);
}
}
export default Import;
// export default DataController;
Under the hood React-Redux is using context and hooks these days, so don't bother implementing a Redux stack until you've outgrown the simpler, React API, or at least you've fixed your issue. Folks joke that Redux is like shooting a fly with a bazooka. More info on React-Redux internals here and here's the documentation for React's Context.
Some psuedo-code to get you on the right path:
// context.js
import { createContext } from 'react';
export const Store = createContext();
// app.js
import React from 'react';
import { Store } from './context';
import Import from './import'; // I wouldn't change the casing on or reuse a reserved keyword personally, maybe calling this something like 'CsvImporter' would be an improvement
function App() {
const [dataDump, setDataDump] = React.useState([]);
return (
<Store.Provider value={{ dataDump, setDataDump }}>
<Import dataDump={dataDump} setDataDump={setDataDump} />
</Store.Provider>
);
}
Now your import component has two new props, dataDump and setDataDump. You can call setDataDump just like any other call to setting state. Nice!
So you need the dataDump in a new component? That's easy peasy, lemon squeezy, and all without global variables or tossing module scoping to the side:
// foobar.js
import React from 'react';
import { Store } from './context';
export function Foobar() {
// you probably want to do more than force render an array as a string, but this is just a proof of concept
return (
<Store.Consumer>
{({ dataDump, setDataDump }) => (
<p>
`${dataDump}`
</p>
)}
</Store.Consumer>
);
}
Just make sure that Foobar or other components are rendered as children of the Provider in app.js and now you have a 'global' context for passing around dataDumps.
This should be super simple for some of you. I have a super simple app that I am making to teach myself the glory that is React and reactDom. Currently, I am pulling from an API (which actually works!), however, I am unable to see any data when rendering to the screen. Literally just two components. I am sure that I am using props or state wrong here, I just don't know where. It's possible that my map function is the problem as well.
Here is the code:
Parent:
import React from "react";
import ReactDOM from "react-dom";
import axios from 'axios'
import { Table } from './Table'
export class DataList extends React.Component {
state = {
articles: []
}
componentDidMount() {
axios.get('http://127.0.0.1:8000/api/portblog/')
.then(res => {
this.setState({
articles: res.data
})
console.log(res.data)
})
}
render() {
return(
<div>
<Table id={this.state.articles.id} articles={this.state.articles} />
</div>
)
}
}
export default DataList
And the child:
import React from "react";
import PropTypes from "prop-types";
import key from "weak-key";
export const Table = (props) => (
<div>
<h1>Welcome to the Article List Page Home</h1>
<li>{props.articles.map((article) => {
{article.titles}
})}</li>
</div>
);
export default Table;
The problem is that your map() call is not returning anything. You need to do something like:
<div>
<h1>Welcome to the Article List Page Home</h1>
{props.articles.map(article =>
<li>{article.titles}</li>
)}
</div>
I'm not exactly sure what your desired output is, but generally you map over data to generate a set of dom elements.
The problem is
<li>{props.articles.map((article) => {
{article.titles}
})}</li>
JSX expressions cannot be used in any arbitrary place. props.articles.map(...) is already an expression, so creating a new one wouldn't make sense.
{article.titles} inside a function creates a block that does nothing. Nothing is returned from map callback, the array isn't mapped to anything.
Depending on what should resulting layout look like, it should be either
<li>{props.articles.map((article) => article.titles)}</li>
output titles within single <li> tag, or
{props.articles.map((article) => <li>{article.titles}</li>)}
to output a list of <li> tags.
ESLint array-callback-return rule can be used to prevent the problem with callback return value.
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,
};
Please bear with me because I am a javascript newbie, and just starting to learn react.
I am trying to make a small app but I keep getting an error that one of my files is not found... specifically this:
bundle.js:56 Uncaught Error: Cannot find module "./components/search_bar"
My file structure is that I have my index.js in a folder called src, then my search bar(search_bar.js) in a folder called components. I have triple checked the spelling on them but I continue to get this error.
This is my index.js
import SearchBar from './components/search_bar';
import React from 'react';
import ReactDOM from 'react-dom';
//Create a componant (some /HTML)
const API_KEY = 'AIzaSyC3Z3qTpvAacDLYEIxaueKflFJbWvdIHsw';
const App = () => {
return (
<div>
<SearchBar />
</div>
);
}
// Put that componant on the page (the DOM)
ReactDOM.render(<App />, document.querySelector('.container'));
And this is my search_bar.js
import React, { Component } from 'react';
class SearchBar extends Component {
contructor(props) {
super(props);
// when user updates the search bar this term will get updated.
this.state = { term: ''};
}
render() {
//update state
//use set state everywhere besides constructor!!
return (
<div>
<input onChange={event => this.setState({term: event.target.value})}
/>
Value of the input: {this.state.term}
</div>
);
}
}
export default SearchBar;
Any Ideas as to what I am doing wrong here?
Can you confirm the following directory structure?
my_project/src/index.js
my_project/src/components/search_bar.js
It seems like your current directory structure might instead look like this:
my_project/src/index.js, my_project/components/search_bar.js
AHHH I left an 's' out of constructor... so search_bar.js was unable to compile. I have been looking at this for about an hour now...