I am working on integrating maps in my react project. I am getting some errors. I used typescript in my project.
Below is a code which shows error:
handleChange = (e:any) => {
this.setState({location: this.state.location})
function initAutocomplete() {
var input = document.getElementById('pac-input');
var searchBox = new window.google.maps.places.SearchBox(input);
searchBox.addListener('places_changed', function() {
this.setState({ PlaceName: document.getElementById('pac-input').value });
});
}
initAutocomplete();
}
This are errors which I am facing:
1.This error is shown at line no.5 in var searchbox last bracket (input)
Argument of type 'HTMLElement | null' is not assignable to parameter of type 'HTMLInputElement'.
Type 'null' is not assignable to type 'HTMLInputElement'.ts(2345)
This error is shown at line no.7 in this.setSatate
Property 'setState' does not exist on type 'SearchBox'.ts(2339)
This error is shown at line no.7 in document.getElementById('pac-input')
Object is possibly 'null'.ts(2531)
This error is shown at line no.7 in last value
Property 'value' does not exist on type 'HTMLElement'.ts(2339)
TypeScript is the most powerful, useful and wonderful thing if you know what you're doing and how to use it. If you don't it will be your worst nightmare.
This is why 3/4 of your errors are because you are using TS wrong with React.
Let me rewrite your code so it will work with TypeScript:
handleChange = (e:any)/* #1 */ => {
this.setState({location: this.state.location}) // #2
function initAutocomplete() {
const input = document.getElementById('pac-input')! as HTMLInputElement //#3 #4;
const searchBox = new window.google.maps.places.SearchBox(input);
searchBox.addListener('change', function() { // THERE IS NO SUCH THING "places_changed" as an event. ~ Would "change" help? ~
this.setState({ PlaceName: document.getElementById('pac-input').value #5 });
});
}
initAutocomplete();
}
#1 Why are you setting event to any? it is not efficient and if you don't use event inside your code - don't put it inside your code - it would be much more nicer to look and read.
#2 Why are you setting the state to it current value? it would reduce the performance of your code because you're trying to render it twice(!) with one function.
There are two "setState" in one function instead of one. each of them re rendering the page.
#3 Why are you using var and not const or let? there are a lot of problems with using var, and since ES2015 const and let replaced var. You should consider not using it.
#4 when you want to assign an element from the DOM TS don't knows if it exits or not so it would return HTMLElement | null. But because you're writing the code you knows there is an Element so you add ! at the end .getElementById('myID')!.
Now it return HTMLElement and not HTMLElement | null.
#5 You should declare Elements before you're using "setState".
like:
const pacInput = document.getElementById('pac-input')! as HTMLInputElement
Now you should tell TS what the Element you are using if it a div, an input, a button etc... so you're writing the type of the element "as HTML(ELEMENT_TYPE_FROM_THE_LIST)";
So it should looks like:
document.getElementById("myID")! as HTMLDivElement;
// Now TS knows the Element exist and a div Element
This code would fix 3 of your code errors.
Let me know about the setState error.
Extra:
If you want to run a function on the fly you should write it as:
(function(){
--DO SOME STUFF
})
Because it is a function on the fly, you don't need to declare nor call it. It will automatically executed even inside other functions.
I really recommend you to learn how to use TS with React. This website has a lot of great sources!
BTW:
If you're using React, Why are you writing elements inside the HTML? This is what JSX for.
You should read more about React JSX here.
Your problems start with the fact that you are not working on a React project, in other words you are not writing your JavaScript as a React frontend but rather a plain JavaScript application.
What do I mean?
In the React view of things, your search bar would be a component, or in other words, your input, would be its own component called SearchBar, so instead of this:
const input = document.getElementById('pac-input')! as HTMLInputElement //#3 #4;
You would write a whole component like this:
import React from 'react';
class SearchBar extends React.Component {
state = { term: '' };
onInputChange = (event) => {
this.setState({ term: event.target.value });
};
onFormSubmit = (event) => {
event.preventDefault();
this.props.onFormSubmit(this.state.term);
}
render() {
return (
<div className="search-bar ui segment">
<form onSubmit={this.onFormSubmit} className="ui form">
<div className="field">
<label>Some Input Field</label>
<input type="text" value={this.state.term} onChange={this.onInputChange} />
</div>
</form>
</div>
);
}
}
export default SearchBar;
Give that a try and if you run into trouble come back and ask a question.
Related
You can see an example of what I am trying to do here: https://codesandbox.io/s/vibrant-leaf-qj8vz
Note: this particular example is using Twin.macro with Styled Components. On my local computer I tried the same thing with the same results using Twin.macro with emotion/next.js.
Here is a sample component illustrating what I am trying to do:
import React from 'react'
import tw from 'twin.macro'
const Acme = ({ children, type }) => <div css={[tw`${type}`]}>{children}</div>
export default Acme
Here is how I would use that component: <Acme type="text-2xl">Text Goes Here</Acme>
My expectation is that I will be able to style this instance of the <Acme /> component via the tailwind css classes that I pass into the type prop. Instead, I get the following error message:
/src/components/Acme.js: twin.macro: Property value expected type of string but got null Learn more: https://www.npmjs.com/package/twin.macro
When trying to figure this out, I noticed something interesting that may be relevant. Here is a variation of the code that does work:
const Acme = ({ children, type }) => {
const typeClass = 'text-2xl'
const typeObj = {
class: 'text-2xl',
}
return <div css={[tw`${typeClass}`]}>{children}</div>
}
export default Acme
Note that I have created a variable typeClass and set it to the same tailwind css class. Note, in particular, the following line of code:
css={[tw`${typeClass}`]}
I have replace the prop type with the variable typeClass. This works. But now, instead of using the variable typeClass let's use the object typeObj that I have created as follows:
const Acme = ({ children, type }) => {
const typeClass = 'text-2xl'
const typeObj = {
class: 'text-2xl',
}
return <div css={[tw`${typeObj.class}`]}>{children}</div>
}
export default Acme
This does not work and produces the same error:
/src/components/Acme.js: twin.macro: Property value expected type of string but got null Learn more: https://www.npmjs.com/package/twin.macro
This is so even though typeClass === typeObj.class evaluates to true.
I don't know if this is helpful, but perhaps it can help indicate a solution. If I can get the type prop to behave like the typeClass variable then hopefully this would work.
Either way, any idea why this is not working and how to fix it?
Thanks.
I found the answer (meaning that someone else answered it on a different site). Here is is. I have to rewrite both the Component and the usage of the component as follows:
// Acme.js
const Acme = ({ children, type }) => <div css={[type]}>{children}</div>
---
// App.js
import tw from "twin.macro"
<Acme type={tw`text-2xl`}>Text Goes Here</Acme>
I have tried this out and it works.
I am new to typescript and not an expert in FE development. I've encountered issue that seems pretty basic, but I failed to found any solution. Maybe I just don't know how to google it properly.
In react component I have a button, that is disabled on some condition, which triggers a component's function:
import React, {Component} from 'react';
type DraftCompany = {
id: null
name: string,
};
type Company = Omit<DraftCompany, 'id'> & {
id: number;
};
type Props = {
company: Company | DraftCompany,
onDeleteCompany: (companyId: number) => void,
}
class CompanyRow extends Component <Props> {
handleDeleteCompany = () => {
this.props.onDeleteCompany(this.props.company.id);
};
render = () => {
return (
<div>
<div>{this.props.company.name}</div>
<div>
<button disabled={this.props.company.id === null} onClick={this.handleDeleteCompany}/>
</div>
</div>
)
}
}
export default CompanyRow;
I am getting typescript error on calling this.props.onDeleteCompany(this.props.company.id); that says that there is a chance I will pass null as a parameter. I fully understand why typescript gives me this error, the question is: what would be the best way to deal with this error?
I have found 3 ways:
1) Add 'if' guard
handleDeleteCompany = () => {
if (this.props.company.id) {
this.props.onDeleteCompany(this.props.company.id);
}
};
It works, but I don't like the idea of adding such guards into every function, if someone removes disabled logic, I want to receive console error telling me about it immediately, not to have it be silently swallowed. In my project I have a lot of such code that relies on render, I doubt it is a best practice to add such checks everywhere. Maybe I am wrong.
2) Apply as to field operator:
handleDeleteCompany = () => {
this.props.onDeleteCompany(this.props.company.id as number);
};
It works, but looks kinda hacky.
3) Apply as operator to whole object and pass it to function:
<button disabled={this.props.company.id === null}
onClick={() => this.handleDeleteCompany(this.props.company as Company)}/>
handleDeleteCompany = (company: Company) => {
this.props.onDeleteCompany(company.id as number);
};
It works, but it looks like I am unnecessary passing the value I could have grabbed in function itself from props. I am not sure it is best practice to do such things.
I am sure there should be some pure typescript solution like defining Props type as a union or using conditional types with some combination of any and never. But I haven't figured it out .
Here is a playground:
playground
You can force the compile to assume a value is never null or undefined with the ! operator:
handleDeleteCompany = () => {
this.props.onDeleteCompany(this.props.company.id!);
};
I think based on your requirement
if someone removes disabled logic, I want to receive console error telling me about it immediately
There is a very simple solution that makes perfect sense, simply change your onDeleteCompany type from (companyId: number) => void to (companyId: number | null) => void, then TypeScript will be happy.
It also semantically make sense to you as you want the runtime report this error when companyId is null. Then you should allow companyId with null to be passed in as parameter.
I'm getting started with a new create-react-app application using TypeScript, hooks, and mobx-react-lite. Despite having used MobX extensively in a React Native app in the past, I've run into an issue that doesn't make any sense to me.
I have a store with two observables: one number and one boolean. There is an initialize() method that runs some library code, and in the success callback, it sets the number and the boolean to different values (see Line A and Line B below).
The issue: my component ONLY re-renders itself when Line A is present. In that case, after the initialization is complete, the 'ready' text appears, and the button appears. If I delete Line B, the 'ready' text still appears. But if I delete Line A (and keep Line B), the button never renders. I've checked things over a hundred times, everything is imported correctly, I have decorator support turned on. I can't imagine why observing a number can trigger a re-render but observing a boolean cannot. I'm afraid I'm missing something horribly obvious here. Any ideas?
The relevant, simplified code is as follows:
// store/app.store.ts
export class AppStore {
#observable ready = false
#observable x = 5
initialize() {
// Takes a callback
ThirdPartyService.init(() => {
this.ready = true
this.x = 10
})
}
}
// context/stores.ts
const appStore = new AppStore()
const storesContext = React.createContext({
appStore
})
export const useStores = () => React.useContext(storesContext)
// App.tsx
const App = observer(() => {
const { appStore } = useStores()
useEffect(() => {
appStore.initialize()
}, [appStore])
return (
<div>
{ appStore.x === 10 && 'ready' } // <-- Line A
{ appStore.ready && <button>Go</button> } // <-- Line B
</div>
)
}
EDIT: A bit more information. I've added some logging statements to just before the return statement for the App component. I also refactored the button conditional to a const. This may provide more insight:
const button = appStore.ready ? <button>Go</button> : null
console.log('render', appStore.ready)
console.log('button', button)
return (
<div className="App">
<header className="App-header">{button}</header>
</div>
)
When appStore.ready is updated, the component does re-render, but the DOM isn't updated. The console shows 'render' true and shows a representation of the button, as it should, but inspecting the document itself shows no button there. Somehow, though, changing the condition from appStore.ready to appStore.x === 10 does update the DOM.
Turns out I didn't quite give complete information in my question. While I was creating a minimal reproduction, I decided to try dropping the top-level <React.StrictMode> component from index.tsx. Suddenly, everything worked. As it happens, mobx-react-lite#1.5.2, the most up-to-date stable release at the time of my project's creation, does not play nice with Strict Mode. Until it's added to a stable release, the two options are:
Remove strict mode from the React component tree
Use mobx-react-lite#next
I'm working on the freeCodeCamp drum machine app. In my app with function arrow components, I set state of display with the useState hook in the parent component and pass it as a prop to the child component. In the parent component, I try to render the display state in a div. However, when the method is triggered (on click of the "drum pad" div), the app crashes. In the console I get an error that says "Uncaught Invariant Violation: Objects are not valid as a React child (found: object with keys {display}). If you meant to render a collection of children, use an array instead."
I've been following along a YouTube tutorial for this project but using arrow function components and Hooks instead of regular classes as used in the tutorial--in the tutorial (around 1:55 of this video) the person successfully does what I'm trying to do, so I think the issue is something to do with using Hooks or arrow function components.
// APP COMPONENT (PARENT)
const sounds = [
{ id: 'snare', letter: 'Q', src: 'https://www.myinstants.com/media/sounds/snare.mp3' },
// etc.
];
const App = () => {
const [display, setDisplay] = useState(''); // <----
const handleDisplay = display => { // <----
setDisplay({ display });
}
return (
<div className="App">
<div className="drum-machine">
<div className="display">
<p>{display}</p> // <---- Related to error in console
</div>
<div className="drum-pads">
{sounds.map(sound => (
<DrumPad
id={sound.id}
letter={sound.letter}
src={sound.src}
handleDisplay={handleDisplay} // <----
/>
))}
</div>
</div>
</div>
);
}
// DRUMPAD COMPONENT (CHILD)
const DrumPad = ({ id, letter, src, handleDisplay }) => {
let audio = React.createRef();
const handleClick = () => {
audio.current.play();
audio.current.currentTime = 0;
handleDisplay(id); // <----
}
return (
<div
className="drum-pad"
id={id}
onClick={handleClick}
>
<p className="letter">{letter}</p>
<audio
ref={audio}
id={letter}
src={src}
>
</audio>
</div>
);
}
You're setting the state as an object instead of a string. Remove the curly brackets around it.
const handleDisplay = display => {
setDisplay(display);
}
This was already answered, but since you are following a tutorial, I am assuming you are learning React and wanted to point a couple of things to help you :)
The incorrect use of state was pointed out, but just for clarification (and the reason I think you were using an object): in the "old" way, with Class components, the state used to be an object, and you needed to update it like an object. This example here shows that. With Hooks, you don't need to set the whole State object, only that specific state property. More info here.
Another point is, in your CodePen example at least, you were missing the import for useState. You either need to import it like this import { useState } from React or use it like this React.useState, since this is a separate module, not imported by default when you import React.
The last point is, when creating components using a loop (like your <DrumPad> with the map) you need to provide a "key" attribute. that will help React keep track of things that needs to be updated or rerendered.
O updated your code with those changes in this link, if you wanna see it working:
https://codesandbox.io/s/reverent-browser-zkum2
Good luck and hope you are enjoying React Hooks :)
I have noticed a difference between the data before returning and after a return of a component.
class AComponent extends Component {
render() {
const body = <BComponent crmStatus={...}/>
debugger // log body on the right
// ... render as static html to electron window
return false
}
}
class BComponent extends Component {
render() {
const resultRender = <article className='large'>...</article>
debugger // log resultRender on the left
return resultRender
}
}
My former question was going to be "How to read rendered component's className?", but I have split the questions as answering what is actually happening and why is it like that really started to bug me and might even give me hints to solve my problem.
So the question is:
What is actually happening to the component and why is it like that? I can have really complicated logic in my render() function, but I guess working with the components isn't that easy.
const headerContact = isContactInCRM ? <p>..</p> : <div>..</div>
const headerCallBtnsOrInfo = isSipEnabled && <div>..buttons..</div>
const callTimer = callDuration && <span>{callDuration}</span>
const footerNotes = <footer>..</footer>
const someImportedComponent = <MyComponent />
const resultRender = <section>
{headerContact}
{headerCallBtnsOrInfo}
{callTimer}
{footerNotes}
{someImportedComponent}
</section>
// there is a difference in data between headerContact and someImportedComponent
// when traversing the resultRender's tree in console
Before answering the question, it's worth to look at what is JSX. It just provides syntactic sugar for the React.createElement(component, props, ...children) function.
<div>
<MyComponent/>
</div>
As an example, above JSX snippet will be transformed to following JavaScript code in the compilation process.
React.createElement(
"div",
null,
React.createElement(MyComponent, null)
);
You can try out this using Babel online repl tool. So if we rewrite your example code using normal JavaScript (after compiling JSX), it will be something like this.
class AComponent extends Component {
render() {
const body = React.createElement(BComponent, { crmStatus: '...' });
debugger // log body on the right
// ... render as static html to electron window
return false
}
}
class BComponent extends Component {
render() {
const resultRender = React.createElement('article',{ className: 'large' }, '...' );
debugger // log resultRender on the left
return resultRender
}
}
By looking at above code, we can understand that <BComponent crmStatus={...}/> doesn't create a new object of BComponent class or call render method of BComponent. It just create a ReactElement with BComponent type and crmStatus prop. So what is a ReactElement? ReactElement is a pain JavaScript object with some properties. I recommend you to read this post from official React blog to get an in-depth understanding of React components, elements, and instances.
An element is a plain object describing a component instance or DOM node and its desired properties. It contains only information about
the component type (for example, a Button), its properties (for
example, its color), and any child elements inside it.
Basically, what you have printed in the console is two React elements in different types. The left one is describing DOM node with type 'article' and the right one is describing BComponent type React component instance. So simply you can't expect them to be the same.
Then where does React create an instance of BComponent? Actually, this happens internally in the React code. Usually, we don't have access to these instances or what return by their render methods in our application code.
However, React still provide an escape hatch called 'refs' which you can explicitly access instances of child components. You might be able to use that approach to solve your original problem.
Hope this helps!