I am new to React TypeScript. I have a problem with the passing state. When I tried to pass the state to a child component and conosle.log(state), I can see the correct object. But, when I tried to do console.log(state.name), I have an error. How can I solve this problem?
App.tsx
export interface Information {
name: string;
age: string;
}
const App: FC = () => {
const [state, setState] = useState<Information | null>({
name: "young",
age: "10",
});
return (
<div className="App">
<div className="header">
<div className="inputContainer">
<input type="text" placeholder="Task.." name="task" />
<input type="number" placeholder="Deadline" name="deadline" />
</div>
<button>Add Task</button>
<div>
<MyForm state={state} />
</div>
</div>
</div>
);
};
Child component
type Props = {
state: ReactNode;
};
const MyForm: FC<Props> = ({ state }: Props) => {
console.log(state.name); // Error
return <div>Hello, {state}</div>;
};
export default MyForm;
Thank you!
The error because you're trying to read the state object inside JSX
return <div>Hello, {state}</div>
Read it like you would with objects instead:
return <div>Hello, {state.name}</div>
Also in your MyForm Component Props, use your Information interface as a type definition instead of ReactNode
export interface Information {
name: string
age: string
}
type Props = {
state: Information
}
Related
I am getting the id which i have to delete but the last line of service.ts that is of delete method is not getting executed...
the files and code snippets I used are : -
COMPONENT.HTML
<li *ngFor="let source of sources$ | async | filter: filterTerm">
<div class="card">
<div class="card-body">
<h5 class="card-title">{{source.name}}</h5>
<p>URL:- <a href ='{{source.url}}'>{{source.url}}</a></p>
<a class="btn btn-primary" href='fetch/{{source.id}}' role="button">fetch</a>
<button class="btn btn-primary" (click)="deleteSource(source.id)">delete </button>
<br>
</div>
</div>
</li>
I tried to console the id geeting from html and the Id i am getting is correct.
//component.ts
export class SourcesComponent implements OnInit {
filterTerm!: string;
sources$ !: Observable<sources[]>;
// deletedSource !: sources;
constructor(private sourcesService: SourcesService) { }
// prepareDeleteSource(deleteSource: sources){
// this.deletedSource = deleteSource;
// }
ngOnInit(): void {
this.Source();
}
Source(){
this.sources$ = this.sourcesService.Sources()
}
deleteSource(id : string){
console.log(id)
this.sourcesService.deleteSource(id);
}
//service.ts
export class SourcesService {
API_URL = 'http://127.0.0.1:8000/sourceapi';
constructor(private http: HttpClient) { }
// let csrf = this._cookieService.get("csrftoken");
// if (typeof(csrf) === 'undefined') {
// csrf = '';
// }
/** GET sources from the server */
Sources() : Observable<sources[]> {
return this.http.get<sources[]>(this.API_URL,);
}
/** POST: add a new source to the server */
addSource(source : sources[]): Observable<sources[]>{
return this.http.post<sources[]> (this.API_URL, source);
//console.log(user);
}
deleteSource(id: string): Observable<number>{
let httpheaders=new HttpHeaders()
.set('Content-type','application/Json');
let options={
headers:httpheaders
};
console.log(id)
return this.http.delete<number>(this.API_URL +'/'+id)
}
}
Angular HTTP functions return cold observables. This means that this.http.delete<number>(this.API_URL +'/'+id) will return an observable, which will not do anything unless someone subscribes to it. So no HTTP call will be performed, since no one is watching the result.
If you do not want to use the result of this call, you have different options to trigger a subscription.
simply call subscribe on the observable:
deleteSource(id : string){
console.log(id)
this.sourcesService.deleteSource(id).subscribe();
}
Convert it to a promise and await it (or don't, if not needed) using lastValueFrom:
async deleteSource(id : string){
console.log(id)
await lastValueFrom(this.sourcesService.deleteSource(id));
}
Problem: UserEvent.type is not working. I have a simple test where I get the textbox, which works, but when trying to type in that textbox, the text does not display.
Most of the solutions I have seen to problems similar to this is to await the typing. I tried that and the text still does not display.
Versions:
"jest": "^28.1.3",
"#testing-library/jest-dom": "^5.16.4",
"#testing-library/react": "12.1.2",
"#testing-library/user-event": "^14.2.1",
Test:
test('LoginPage should handle events properly', async () => {
render(<LoginPage />)
const textbox = screen.getByRole(/textbox/i)
await userEvent.type(textbox, 'test')
screen.debug(textbox)
})
Output from screen.debug() after firing the typing:
<textarea
placeholder="enter your private key here."
/>
LoginPage Component:
import { useHistory } from 'react-router-dom'
import { useContext, useState, useEffect } from 'react'
import { ABOUT_URL } from '../../config'
import LoadingCover from '../../components/loadingCover/loadingCover'
import LoadingButton from '../../components/loadingButton/loadingButton'
import UserContext from '../../context/User'
const LoginPage = () => {
const history = useHistory()
const [isLoading, setIsLoading] = useState(false)
const [input, setInput] = useState<string>('')
const [errorMsg, setErrorMsg] = useState<string>('')
const [isButtonLoading, setButtonLoading] = useState<boolean>(false)
const userContext = useContext(UserContext)
useEffect(() => {
setErrorMsg('')
}, [input])
const handleInput = (event: any) => {
setInput(event.target.value)
}
const login = async () => {
setButtonLoading(true)
const hasSignedUp = await userContext.login(input)
setButtonLoading(false)
if (!hasSignedUp) {
setErrorMsg('Incorrect private key. Please try again.')
return
}
// need to conditionally get an airdrop if the user signed up but has
// not claimed an airdrop, and it's the same epoch as when they signed
// up
history.push('/')
}
return (
<div className="login-page">
<div className="left-column">
<img
src={require('../../../public/images/unirep-title-white.svg')}
/>
</div>
<div className="right-column">
<div className="close">
<img
id="unirep-icon"
src={require('../../../public/images/unirep-title.svg')}
/>
<img
id="close-icon"
src={require('../../../public/images/close.svg')}
onClick={() => history.push('/')}
/>
</div>
<div className="info">
<div className="title">Welcome back</div>
<p>
To enter the app, please use the private key you got
when you signed up.
</p>
<textarea
placeholder="enter your private key here."
onChange={handleInput}
/>
{errorMsg.length === 0 ? (
<div></div>
) : (
<div className="error">{errorMsg}</div>
)}
<div className="sign-in-btn" onClick={login}>
<LoadingButton
isLoading={isButtonLoading}
name="Sign in"
/>
</div>
<div className="notification">
Lost your private key? Hummm... we can't help you to
recover it, that's a lesson learned for you. Want to
restart to earn rep points?{' '}
<a
target="_blank"
href={`${ABOUT_URL}/alpha-invitation`}
>
Request an invitation code here.
</a>
</div>
<div className="go-to-signup">
Got an invitation code? Join here
</div>
</div>
</div>
{isLoading ? <LoadingCover /> : <div></div>}
</div>
)
}
export default LoginPage
Got the same issue and find a really simple solution on the Testing Library documentation :
Start your test with
const user = userEvent.setup()
then you call userEvent methods directly from user :
await user.type(textbox, 'test')
You have to setup directly on each test, the library recommend to avoid using beforeEach() for this.
Ember and Braintree Hosted Fields are not a good mix so far, Braintree Support are out of ideas on this one. When the form renders on the page it calls the action to create the client. The client is undefined.
picture-this-44ac48bef9f8df633632a4d202da2379.js:57 Uncaught TypeError: Cannot read property 'client' of undefined
component hbs
<script src="https://js.braintreegateway.com/web/3.81.0/js/client.min.js"></script>
<script src="https://js.braintreegateway.com/web/3.81.0/js/hosted-fields.min.js"></script>
<div class="demo-frame" {{did-insert this.setupBraintreeHostedFields}}>
<form action="/" method="post" id="cardForm" >
<label class="hosted-fields--label" for="card-number">Card Number</label>
<div id="card-number" class="hosted-field"></div>
<label class="hosted-fields--label" for="expiration-date">Expiration Date</label>
<div id="expiration-date" class="hosted-field"></div>
<label class="hosted-fields--label" for="cvv">CVV</label>
<div id="cvv" class="hosted-field"></div>
<label class="hosted-fields--label" for="postal-code">Postal Code</label>
<div id="postal-code" class="hosted-field"></div>
<div class="button-container">
<input type="submit" class="button button--small button--green" value="Purchase" id="submit"/>
</div>
</form>
</div>
component class
import Component from '#glimmer/component';
import { action } from '#ember/object';
import { inject as service } from '#ember/service';
import { tracked } from '#glimmer/tracking';
import { braintree } from 'braintree-web';
export default class CardPaymentComponent extends Component {
#action
setupBraintreeHostedFields() {
alert('booh');
var form = document.querySelector('#cardForm');
var authorization = 'sandbox_24nzd6x7_gyvpsk2myght4c2p';
braintree.client.create({
authorization: authorization
}, function(err, clientInstance) {
if (err) {
console.error(err);
return;
}
createHostedFields(clientInstance);
});
function createHostedFields(clientInstance) {
braintree.hostedFields.create({
client: clientInstance,
styles: {
'input': {
'font-size': '16px',
'font-family': 'courier, monospace',
'font-weight': 'lighter',
'color': '#ccc'
},
':focus': {
'color': 'black'
},
'.valid': {
'color': '#8bdda8'
}
},
fields: {
number: {
selector: '#card-number',
placeholder: '4111 1111 1111 1111'
},
cvv: {
selector: '#cvv',
placeholder: '123'
},
expirationDate: {
selector: '#expiration-date',
placeholder: 'MM/YYYY'
},
postalCode: {
selector: '#postal-code',
placeholder: '11111'
}
}
}, function (err, hostedFieldsInstance) {
var tokenize = function (event) {
event.preventDefault();
hostedFieldsInstance.tokenize(function (err, payload) {
if (err) {
alert('Something went wrong. Check your card details and try again.');
return;
}
alert('Submit your nonce (' + payload.nonce + ') to your server here!');
});
};
form.addEventListener('submit', tokenize, false);
});
}
}
}
package.json
...
"ember-cli": "^3.25.2",
"braintree-web": "^3.81.0",
...
** Final Solution **
NPM braintree-web not required. Component class does not have access to the Braintree Window object. Move the tags to the app/index.html as outlined in the accepted answer.
component hbs
<article class="rental">
<form action="/" method="post" id="cardForm">
<label class="hosted-fields--label" for="card-number">Cardholder Name</label>
<div id="card-holder-name" class="hosted-field payment"></div>
<label class="hosted-fields--label" for="card-number">Email</label>
<div id="email" class="hosted-field payment"></div>
<label class="hosted-fields--label" for="card-number">Card Number</label>
<div id="card-number" class="hosted-field payment"></div>
<label class="hosted-fields--label" for="expiration-date">Expiration Date</label>
<div id="expiration-date" class="hosted-field payment"></div>
<label class="hosted-fields--label" for="cvv">CVV</label>
<div id="cvv" class="hosted-field payment"></div>
<label class="hosted-fields--label" for="postal-code">Postal Code</label>
<div id="postal-code" class="hosted-field payment"></div>
<div class="button-container">
<input type="submit" class="button" value="Purchase" id="submit"/>
</div>
</form>
</article>
<script>
var form = document.querySelector('#cardForm');
var authorization = 'sandbox_24nzd6x7_gyvpsk2myght4c2p';
braintree.client.create({
authorization: authorization
}, function(err, clientInstance) {
if (err) {
console.error(err);
return;
}
createHostedFields(clientInstance);
});
function createHostedFields(clientInstance) {
braintree.hostedFields.create({
client: clientInstance,
styles: {
'input': {
'font-size': '1.2em',
'font-family': 'courier, monospace',
'font-weight': 'lighter',
'color': '#ccc'
},
':focus': {
'color': 'black'
},
'.valid': {
'color': '#8bdda8'
}
},
fields: {
number: {
selector: '#card-number',
placeholder: '4111 1111 1111 1111'
},
cvv: {
selector: '#cvv',
placeholder: '123'
},
expirationDate: {
selector: '#expiration-date',
placeholder: 'MM/YYYY'
},
postalCode: {
selector: '#postal-code',
placeholder: '11111'
}
}
}, function (err, hostedFieldsInstance) {
var tokenize = function (event) {
event.preventDefault();
hostedFieldsInstance.tokenize(function (err, payload) {
if (err) {
alert('Something went wrong. Check your card details and try again.');
return;
}
alert('Submit your nonce (' + payload.nonce + ') to your server here!');
});
};
form.addEventListener('submit', tokenize, false);
});
}
</script>
You can use Braintree SDK via either the direct script tag or using the npm module with the help of ember-auto-import. In your case, you are using both.
For simplicity, let's use the script tag to inject the SDK. The issue in your snippet is that you are trying to load the script tag inside a component handlebar file. the handlebars (.hbs file) cannot load scripts using a <script> tag. We need to move the script tag to the index.html file present inside the app folder. This will load the SDK properly to be used inside a component.
app/index.html:
<body>
...
<script src="https://js.braintreegateway.com/web/3.81.0/js/client.min.js"></script>
<script src="https://js.braintreegateway.com/web/3.81.0/js/hosted-fields.min.js"></script>
{{content-for "body-footer"}}
</body>
Once you inject the SDK properly, you can use the braintree window object without any issue.
Sorry if I am asking a beginner's level question. I am new to React.js and recently I have been trying to grasps the concepts by following this tutorial:
JustDjango
What I am trying to accomplish is creating a login form which uses redux to store the states, my code is as follows :
import React from 'react';
import { Form, Icon, Input, Button, Spin } from 'antd/lib';
import { connect } from 'react-redux';
import { NavLink } from 'react-router-dom';
import * as actions from '../store/actions/auth';
const FormItem = Form.Item;
const antIcon = <Icon type="loading" style={{ fontSize: 24 }} spin />;
class NormalLoginForm extends React.Component {
handleSubmit = (e) => {
e.preventDefault();
this.props.form.validateFields((err, values) => {
if (!err) {
this.props.onAuth(values.userName, values.password);
this.props.history.push('/');
}
});
}
render() {
let errorMessage = null;
if (this.props.error) {
errorMessage = (
<p>{this.props.error.message}</p>
);
}
const { getFieldDecorator } = this.props.form;
return (
<div>
{errorMessage}
{
this.props.loading ?
<Spin indicator={antIcon} />
:
<Form onSubmit={this.handleSubmit} className="login-form">
<FormItem>
{getFieldDecorator('userName', {
rules: [{ required: true, message: 'Please input your username!' }],
})(
<Input prefix={<Icon type="user" style={{ color: 'rgba(0,0,0,.25)' }} />} placeholder="Username" />
)}
</FormItem>
<FormItem>
{getFieldDecorator('password', {
rules: [{ required: true, message: 'Please input your Password!' }],
})(
<Input prefix={<Icon type="lock" style={{ color: 'rgba(0,0,0,.25)' }} />} type="password" placeholder="Password" />
)}
</FormItem>
<FormItem>
<Button type="primary" htmlType="submit" style={{marginRight: '10px'}}>
Login
</Button>
Or
<NavLink
style={{marginRight: '10px'}}
to='/signup/'> signup
</NavLink>
</FormItem>
</Form>
}
</div>
);
}
}
const WrappedNormalLoginForm = Form.useForm()(NormalLoginForm);
const mapStateToProps = (state) => {
return {
loading: state.loading,
error: state.error
}
}
const mapDispatchToProps = dispatch => {
return {
onAuth: (username, password) => dispatch(actions.authLogin(username, password))
}
}
export default connect(mapStateToProps, mapDispatchToProps)(WrappedNormalLoginForm);
The error traceback shows that the error is coming from :
76 | const WrappedNormalLoginForm = Form.useForm()(NormalLoginForm);
77 |
78 | const mapStateToProps = (state) => {
79 | return {
Some google search on this particular error shows that this error has something to do with hooks being defined in a classed based component , however i do not understand why :
const mapStateToProps = (state) => {......
is considered a hook
Will greatly appreciate anybody's help!
React hooks only used by functional components. You used class components.
Shortly, Form.useForm() the method is only used functional components, you can read it from this link below:
https://ant.design/components/form/
I'm just creating my first custom component, and I'm really struggling with the basics. My component:
<template>
<StackLayout>
<Label :text="title" />
<Label :text="slate.description" />
</StackLayout>
</template>
<script>
var slate;
export default {
name: "SlateComponent",
props:
['slate', 'title'],
data() {
return {
slate: slate,
};
},
}
</script>
This component is to be updated regularly, and occupy a good chunk of the app home page:
<template>
<Page class="Page" actionBarHidden="true" backgroundSpanUnderStatusBar="true" >
<StackLayout>
<StackLayout row="0">
...
</StackLayout>
<StackLayout row="1">
<SlateComponent :title="title" :slate="slate" />
</StackLayout>
...
</Page>
</template>
<script>
...
import SlateComponent from "./SlateComponent";
var slateTitle;
var title;
var gameSlates;
var currentSlate;
var slate;
data() {
return {
events: events,
title: title,
slate: slate,
};
},
async created() {
this.gameSlates = await getGameSlates();
this.currentSlate = this.gameSlates[2];
this.title = this.currentSlate.description;
console.info("The title is: " + this.title);
this.slate = this.currentSlate;
}
};
Result: No matter what I do, no props object passes to the component.
If I comment out the
the app compiles and runs fine, logs currentSlate or its property, description and displays the component, including title.
But, when I include that line, it blows up, with the error: slate is undefined.
(I know that
props:
['slate', 'title'],
is not proper according to the style guide. But I couldn't get the preferred format to work either.)
What am I missing here?
When accessing props anywhere outside of the template, this is required
data() {
return {
slate: slate,
};
}
Should be
data() {
return {
slate: this.slate
};
},