I'm building a search engine (with React.js), where I can look for GIPHY gifs, using their API. I'm new to React.js and i'm having some trouble getting this code right.
import React from 'react'; //react library
import ReactDOM from 'react-dom'; //react DOM - to manipulate elements
import './index.css';
import SearchBar from './components/Search';
import GifList from './components/SelectedList';
class Root extends React.Component { //Component that will serve as the parent for the rest of the application.
constructor() {
super();
this.state = {
gifs: []
}
}
handleTermChange(term) {
console.log(term);
//---------------------->
let url = 'http://api.giphy.com/v1/gifs/search?q=${term}&api_key=dc6zaTOxFJmzC';
fetch(url).
then(response => response.json()).then((gifs) => {
console.log(gifs);
console.log(gifs.length);
this.setState({
gifs: gifs
});
});//<------------------------- THIS CODE HERE
};
render() {
return (
<div>
<SearchBar onTermChange={this.handleTermChange} />
<GifList gifs={this.state.gifs} />
</div>
);
}
}
ReactDOM.render( <Root />, document.getElementById('root'));
I get the following error in the console:
Uncaught (in promise) TypeError: _this2.setState is not a function
at eval (index.js:64)
at
Any help is appreciated :) Thanks!
this is not auto-bound in ES6 style syntax.
You have to bind in the constructor:
```
super();
this.state = {
gifs: []
}
this.handleTermChange = this.handleTermChange.bind(this)```
or use arrow function syntax for the function in question:
func = () => {};
For reference: https://facebook.github.io/react/blog/2015/01/27/react-v0.13.0-beta-1.html#autobinding
Related
Wassup Guys,
I have a reusable component, that translates keys into a choosen language through keynames as string or a binded var.
Usually I use a tag for this, but because of different reasons I am switching/replacing current translations with {t('...')}.
Here is the code of the component:
import React from 'react';
import { useTranslation as defaultTranslation } from 'react-i18next';
import i18next from 'i18next';
export const useTranslation = (ns = 'common', options) => {
return defaultTranslation(ns, {
useSuspense: false,
...options,
});
};
export const withTranslation = (Component, ns, options) => {
const TranslatedHOC = (props) => {
const translationProps = useTranslation(ns, options);
return <Component {...translationProps} {...props} />;
};
return TranslatedHOC;
};
export const getCurrentLanguage = () =>
i18next.language || localStorage.getItem('language') || 'de-DE';
First of all I define the const for the used imported function:
const {t} = useTranslation();
normal case: importing my component in the file, where I want to use it and add code above.
Code of my component, where I want to replace the Tags.
// Import React Table
import ReactTable from 'react-table';
import 'react-table/react-table.css';
import LocalizedText from '#components/i18n/LocalizedText';
class T extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [],
pages: null,
loading: true,
};
this.fetchData = this.fetchData.bind(this);
}
fetchData(state, instance) {
this.props.onFetchData(state, instance).then(() => {
this.setState({
loading: false,
});
});
}
render() {
return (
<ReactTable
{...this.props}
previousText={
<LocalizedText textKey="caseoverview.orderproduct.back" />
}
nextText={
<LocalizedText textKey="caseoverview.orderproduct.continue" />
}
loadingText={<LocalizedText textKey="loading" />}
noDataText={<LocalizedText textKey="placeholders.nodata" />}
pageText={
<LocalizedText textKey="reservationcalculator.search.result.page" />
}
ofText={<LocalizedText textKey="from" />}
rowsText={<LocalizedText textKey="rows" />}
className="case-search-table"
/>
);
}
}
export default T;
...
previousText={
<LocalizedText textKey="caseoverview.orderproduct.back" />
}
...
should change to:
...
previousText={
t('caseoverview.orderproduct.back')
}
...
The problem is, that I can't use the code quoted above without getting any issues regarding invalid hook calls. If I move it out somehow, I get errors telling me that my 't' is either not defined or an unexpected token. Could someone help me out? Searched online for solutios without any result.
A hook can only be used in a functional component. You can change this class component to a functional component, or you can use react-i18next's withTranslation HOC to wrap your class component. See https://react.i18next.com/latest/withtranslation-hoc#when-to-use
#kevin asworth answer helped me out.
Using withTranslation with passing t as prop
const {t} = this.props;
inside the render method worked for me.
I found a library I want to use in my project, but it's a plain JavaScript library and doesn't know anything about React.
https://www.npmjs.com/package/json-formatter-js
Is it possible to use this in my React project? I tried this, but the render crashes.
class JSONView extends Component {
constructor(props) {
super(props);
}
render() {
const formatter = new JSONFormatter(this.props.data);
const Rendered = formatter.render();
return (
<div>
<Rendered />
</div>
);
}
}
The error I get is this.
Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object.
The typeof Rendered is object.
Try this
class JSONView extends React.Component<any, any> {
constructor(props) {
super(props);
}
public refs:{
JsonDiv: HTMLDivElement;
};
componentDidMount(){
const formatter = new JSONFormatter(this.props.data);
this.refs.JsonDiv.appendChild(formatter.render());
}
render() {
return (
<div ref='JsonDiv'>
</div>
);
}
}
or this one
class JSONView extends React.Component<any, any> {
constructor(props) {
super(props);
}
render() {
const formatter = new JSONFormatter(this.props.data);
return (
<div dangerouslySetInnerHTML={formatter.render()}>
</div>
);
}
}
Looks like the library returns HTMLDOMElement from function formatter.render().
So you can't use JSX syntax to render it.
While I agree that the other answers can work, I would prefer using a dom ref and appending the returned HTMLDOMElement from formatter.render() in componentDidMount lifecycle method.
import React, { Component } from 'react';
import { render } from 'react-dom';
import JSONFormatter from "json-formatter-js";
import Hello from './Hello';
import './style.css';
class JSONView extends Component {
constructor(props) {
super(props);
}
ref = null;
componentDidMount(){
const formatter = new JSONFormatter(this.props.data);
const Rendered = formatter.render();
this.ref.appendChild(Rendered);
}
render() {
return (
<div ref={e => this.ref = e}>
</div>
);
}
}
render(<JSONView data={{Hello: {Hello: "World"}}} />, document.getElementById('root'));
Working Demo You can drill down to the object.
In the other approach which makes use of dangerouslySetInnerHTML or just rendering the string content, you have the risk of losing DOM events.
import React, { Component } from 'react';
import { render } from 'react-dom';
import JSONFormatter from "json-formatter-js";
import Hello from './Hello';
class JSONView extends Component {
constructor(props) {
super(props);
}
ref = null;
render() {
const formatter = new JSONFormatter(this.props.data);
console.log(formatter);
const Rendered = formatter.render();
console.log(Rendered.innerHTML);
return (
<div>
<div dangerouslySetInnerHTML={{__html:Rendered.innerHTML}} />
</div>
);
}
}
render(<JSONView data={{Hello: {Hello: "World"}}} />, document.getElementById('root'));
Working Link You cannot drill down to the object.
edit
try
{ Rendered }
there might be warning if it ships class with html tags.
between curly braces {} and it'll work (99 %) and if it doesn't then continue reading.
you can render if it returns string.
currently it might b returning html object which is incompatible with react dom render.
if it contains class property then it'll cause problem even if it's in string format.
you can use react-html-parser to render html strings in react. reference
const Rendered = formatter.render().outerHTML // this will give you html string instead of html object
now use react-html-parser if you don't wanna manage class attribute name conflict as react accepts className
import ReactHtmlParser from 'react-html-parser';
class JSONView extends Component {
constructor(props) {
super(props);
}
render() {
const formatter = new JSONFormatter(this.props.data);
const Rendered = formatter.render().outerHTML;
return (
<div>
{ ReactHtmlParser(Rendered) }
</div>
);
}
}
I'm trying to add a component to a default .NET Core MVC with React project. I believe I have everything wired up to mirror the existing "Fetch Data" component, but it doesn't seem like it's actually being called (but the link to the component in my navbar does move to a new page).
The component itself...
import React, { Component } from 'react';
export class TestComponent extends Component {
static displayName = TestComponent.name;
constructor (props) {
super(props);
console.log("WHO NOW?");
this.state = { message: '', loading: true, promise: null };
this.state.promise = fetch('api/SampleData/ManyHotDogs');
console.log(this.state.promise);
}
static renderForecastsTable (message) {
return (
<h1>
Current Message: {message}
</h1>
);
}
render () {
let contents = this.state.loading
? <p><em>Loading...</em></p>
: TestComponent.renderForecastsTable(this.state.message);
return (
<div>
<h1>Weather forecast</h1>
<p>This component demonstrates fetching data from the server.</p>
{contents}
</div>
);
}
}
The App.js
import React, { Component } from 'react';
import { Route } from 'react-router';
import { Layout } from './components/Layout';
import { Home } from './components/Home';
import { FetchData } from './components/FetchData';
import { Counter } from './components/Counter';
import { TestComponent } from './components/TestComponent';
export default class App extends Component {
static displayName = App.name;
render () {
return (
<Layout>
<Route exact path='/' component={Home} />
<Route path='/counter' component={Counter} />
<Route path='/fetch-data' component={FetchData} />
<Route path='/test-controller' component={TestComponent} />
</Layout>
);
}
}
That console.log("Who now") is never called when I inspect, and the page remains totally blank. I can't find a key difference between this and the functioning components, and google has not been much help either. Any ideas what is missing?
Edit
While troubleshooting this, I ended up creating a dependency nightmare that broke the app. Since I'm only using the app to explore React, I nuked it and started over--and on the second attempt I have not been able to reproduce the not-rendering issue.
It is advisable to use componentDidMount to make the call to the REST API with the fetch or axios.
class TestComponent extends Component{
constructor(props){
state = {promise: ''}
}
async componentDidMount () {
let promise = await fetch ('api / SampleData / ManyHotDogs');
this.setState ({promise});
console.log (promise);
}
render(){
return(
<div>{this.state.promise}</div>
);
}
}
This is all my code below .
When I run it I receive this error (TypeError: Cannot read property 'todos' of null )todos not found at this line var todos=this.state.todos;
My App.js file
import React, { Component } from 'react';
class App extends Component {
getInitialState (){
return{
todos:['washup',"hi","hello","up"]
}
}
render() {
var todos=this.state.todos;
Added Code here
todos=todos.map(function(item,index){
return(
<li>item</li>
);
}
);
Till here
return (
<div id="App">
<ul>{todos}</ul>
)
} )
</div>
);
}
}
export default App;
This is my index.js file
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import registerServiceWorker from `'./registerServiceWorker';`
ReactDOM.render(<div>
<App>Here is my Buttonas</App>
</div>, document.getElementById('root'));
registerServiceWorker();
EDIT
New Error
TypeError: Cannot read property 'map' of undefined
At this line todos=todos.map(function(item,index){
What is the error now?
getInitialState() is only used with createReactClass(). When using ES6 classes you just set state as a property:
See Setting the Initial State in the react docs:
In ES6 classes, you can define the initial state by assigning
this.state in the constructor:
class App extends Component {
constructor(props) {
super(props);
this.state = {
todos: ['washup',"hi","hello","up"],
}
}
// ...
}
or just
class App extends Component {
state = {
todos: ['washup',"hi","hello","up"],
}
// ...
}
With createReactClass(), you have to provide a separate
getInitialState method that returns the initial state:
var App = createReactClass({
getInitialState: function() {
return {
todos: ['washup',"hi","hello","up"],
};
},
// ...
});
You're initializing the state older way in a newer version of reactjs. I already appreciate the answer of trixn. But here's also a solution without removing your current code:
class App extends Component {
state = getInitialState (){
return{
todos:['washup',"hi","hello","up"]
}
}
Notice that I have assigned state to the getInitialState and will work fine because this returns the object {todos:['washup',"hi","hello","up"]} which is similar to this:
state = {todos:['washup',"hi","hello","up"]}
Next, when your component is being rendered first time your todos might get undefined as you stated. To resolve this issue you may add a condition:
todos && todos.length && todos.map(...)
Now, the map function will only run if the todos is not undefined and it has length ie. it has at least one value.
It caused because you didn't define todos in your state, to achieve the soloution, make a constructor in your class and set a todos variable in your state, you can set in empty or null in the constructor and fill it later, then you can use it in your render section, comment if you need further information and also read react life cycle in the official website
With createClass you can use getInitialState:
const App = React.createClass({
getInitialState() {
return { /* initial state */ };
},
});
but with ES6 classes you do like this:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {todos:['washup',"hi","hello","up"]};
}
}
EDITED: get items through map:
class App extends Component {
state={
todos: ['washup', "hi", "hello", "up"]
}
render() {
var todos= this.state.todos.map((item)=>{
return <li>{item}</li>
})
return (
<div id="App">
<ul>{todos}</ul>
</div>
)
}
}
Try this. You should define you todos in the state
App.Js
import React, { Component } from 'react';
import { render } from 'react-dom';
import Hello from './Hello';
import './style.css';
class App extends Component {
constructor(props) {
super(props);
this.state ={
todos:['washup',"hi","hello","up"]
}
}
render() {
return (
<div id="App">
<ul>{this.state.todos}</ul>
</div>
);
}
}
export default App;
I have a project using reactjs, which is transpiled by babel. I use the es2015 and react transforms in my .babelrc. I am currently refactoring and in my first pass I basically did export class foo for everything I needed. A lot of these classes should really just be functions, so I am trying to rewrite them as such, but I keep getting the same error. My main application file looks somethings like this:
import React, { Component } from 'react';
import {Foo, Bar} from './components/ui.js';
class Application extends Component {
constructor(props){
super(props);
this.state = {
object: null
}
}
componentDidMount(){
// code
}
componentDidUpdate(){
// other code
}
render(){
return(
<div>
<Foo />
<Bar />
</div>
)
}
}
module.exports = Application
And my import from ui.js is like this:
import React, { Component } from 'react';
export class Foo extends Component {
constructor(props){
super(props);
}
render() {
return (
// Some JSX
)
}
}
export class Bar extends Component {
constructor(props){
super(props);
}
render() {
return (
// Some other JSX
)
}
}
When I try and change one of these exported classes to a function, for example:
// Note: I have tried a variety of syntax such as function, const, etc...
export var Bar {
render() {
return (
// Some other JSX
)
}
}
I get the following error:
SyntaxError: Unexpected token <line where I declare a function>
I am not sure what I am doing wrong, and my google searches are only coming up with answers to other problems.
It's the same as defining the function as a variable but just adding export to the front e.g. (using ES6 syntax)
export const render = () => (
// Some other JSX
);
or alternatively
export var render = function() {
return (
// Some other JSX
);
};
Exporting functions is no different than exporting class. Basic rules must be followed .
Function/Class name should in CAPS
There will be only one "export" line .
Every function return body should have a single tag encompassing other parts. Most commonly used is a tag .
This usually works: import App from "./App"; where App.js is my jsx file.
You can do an explicit import too . : import AllApp from "./classhouse.jsx";
Name of the js/jsx file does not matter. It can be anycase (lower, upper).
For returning multiple functions from one file, you need to create one more function , that encompasses all other functions .
See the example below showing multiple functions returned.
import React from 'react';
/* All function / class names HAS TO BE in CAPS */
var App1 = function (){
return (
<div>
<h1>
Hello World
</h1>
</div>
)
}
var App2 = function (){
return (
<div>
<h1>World Number 2 </h1>
</div>
);
}
var AllApp = function (){
return (
<div>
<App1 />
<App2 />
</div>
);
}
export default AllApp;
My index.js file:
import React from 'react';
import ReactDOM from "react-dom";
import AllApp from "./classhouse.jsx"; /* Note: App name has to be in CAPS */
import App from "./App";
const jsx =
<div>
<AllApp />
<App />
</div>
ReactDOM.render(jsx, document.getElementById("root"));
You are writing functional components in wrong way.
function Welcome() {
return <h1>Hello World</h1>;
}
or
const Welcome = () => {
return <p>Hello Wrold</p>
}
export default Welcome ;
ES6 doesn't allow export default const. You must declare the constant first then export it.