I am rendering modal using react and redux.
I've seen thousand of examples of how to create a modal, but none on how to fire it inside another component.
I took the same approach to render modals on redux
on this question
My store is this:
export const store=createStore(
rootReducer,
compose(
applyMiddleware(thunk)
))
And the main component:
class Main extends React.Component {
render () {
return(
<BrowserRouter>
<Provider store={store} >
<App/>
</Provider>
</BrowserRouter>
)
}}
The app component is where I am guessing I should render the modal
class App extends React.Component {
render () {
return(
<div className="main-app">
<Home />
<Modal />
</div>
)
}}
But how can I update the state of this Modal component from within the components inside <Home >
My modal component looks like this:
import LoginModal from './LoginModal';
const MODAL_COMPONENTS = {
'LOGIN': LoginModal
}
class ModalRoot extends React.Component {
render() {
const SpecificModal = MODAL_COMPONENTS[this.props.modal.modalType];
if(!this.props.modal.showModal) return <SpecificModal />
return null
}
}
let mapStateToProps=state=>{
return {
modal: state.modal
}
}
export default connect(mapStateToProps)(ModalRoot);
Which will be the best approach to change the state three (store) of my redux app to change the state of the modal?
Thanks
Suppose you want to trigger the modal by clicking a button in Home button. You can pass in dispatch function to Home using mapDispatchToProps and dispatch action that changes the state of modal from there.
actions.js
function changeModal(payload) {
return {
type: 'CHANGE_MODAL',
payload,
}
}
reducer
// below defines what's in store.modal
function modalReducer(state = {}, action) {
switch(action.type) {
case: 'CHANGE_MODAL':
return {
...state,
...action.payload
}
// ... rest of your code
}
}
Home component
class Home extends Component {
//... rest of logic
changeModal = (modal) => {
const currentModal = {
showModal: true,
modalType: modal,
}
this.props.changeModal({ modal: currentModal });
}
render() {
// im using 2 buttons to trigger different modals,
return <div>
<button onClick={() => this.changeModal('HOME')}>trigger home modal</button>
<button onClick={() => this.changeModal('OTHER')}>trigger other modal</button>
</div>
}
}
const mapDispatchToProps = (dispatch) => ({
changeModal: (payload) => dispatch(changeModal(payload))
});
// insert mapDispatchToProps as a second argument to connect
// home component may or may not have mapStateToProps
export default connect(mapStateToProps, mapDispatchToProps)(Home);
So when you press the button, the state.modal will change and it will show the modal depending on the new state.
Related
I am not able to figure out how to inform a new registering user that email is already registered below the input element on entering email on my front-end in angular. Please assist. I am able to retrieve my async validation response as to whether the email is already registered or is available. I am using PrimeNG components and scss
In my Network Response, API response, I see {"email":"This eamil is already registered"} if the email is already registered and if the email is not registered, {"available":true}.
Below is the code for async validator in my unique-email.ts.
export class UniqueEmail implements AsyncValidator {
constructor(private authService: AuthService) {}
validate = (control: AbstractControl) => {
const { value } = control;
return this.authService.emailAvailable(value).pipe(
map((value) => {
if (value.available) {
return null;
}
}),
catchError((err) => {
if (err.error.email) {
return of({ EmailInUse: true });
} else {
return of({ noConnection: true });
}
})
);
};
}
Below is my code for AuthService.ts
interface EmailAvailableResponse {
available: boolean;
}
export class AuthService {
apiURLAuth = environment.apiUrl + 'users';
constructor(private http: HttpClient) {}
emailAvailable(email: string) {
return this.http.post<EmailAvailableResponse>(`${this.apiURLAuth}/emailexist`, {
email
});
}
}
My signup.component.ts
export class SignupComponent implements OnInit {
signupFormGroup: FormGroup;
isSubmitted = false;
authError = false;
constructor(
private matchPassword: MatchPassword,
private uniqueEmail: UniqueEmail,
private formBuilder: FormBuilder,
private authService: AuthService,
private localstorageService: LocalstorageService
) {}
private _initSignupForm() {
this.signupFormGroup = this.formBuilder.group(
{
email: ['', [Validators.required, Validators.email], [this.uniqueEmail.validate]]
}
)
}
get isignupForm() {
return this.signupFormGroup.controls;
}
}
<form [formGroup]="signupFormGroup">
<div class="col-12">
<div class="p-inputgroup">
<span class="p-inputgroup-addon"><i class="pi pi-id-card"></i></span>
<input type="text" formControlName="email" pInputText placeholder="Email" />
</div>
<small *ngIf="isignupForm.email.invalid && isSubmitted" class="p-error"
><span *ngIf="isignupForm.email.errors.required">Email is required</span>
<span *ngIf="isignupForm.email.errors.email">Email is Invalid</span>
<span *ngIf="isignupForm.email.errors.email.EmailInUse"
>Email is already registered to another user. Try another email account</span
>
</small>
</div>
</form>
There is an error for
isignupForm.email.errors.email.EmailInUse
Instead, use:
<span *ngIf="isignupForm.email.errors.EmailInUse">
Email is already registered to another user. Try another email account
</span>
Sample Demo on StackBlitz
Followed steps in AngularFire Quickstart
Add authentication as described in 5. Getting started with Firebase Authentication
When I set my Firestore rules to limit read and write access to authenticated users, I either get an error or I get nothing. For more details, see Issue #2838 filed in the GitHub repository.
My environment is:
Angular CLI: 12.0.1
Node: 14.17.0
Package Manager: npm 7.13.0
AngularFire: 6.1.5
Firebase: 8.6.1
Firebase Tools: 9.11.0
OS: Ubuntu 20.04.2 LTS (linux x64)
My Firestore rules are:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read: if request.auth != null;
allow write: if request.auth != null;
}
}
}
app.component.ts
import { Component, OnInit } from '#angular/core';
import { AngularFirestore } from '#angular/fire/firestore';
import { AngularFireAuth } from '#angular/fire/auth';
import firebase from 'firebase/app';
import { Observable } from 'rxjs';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
title = 'Angular Fire Quickstart';
userDisplayName: string | null = null;
leagues$: Observable<any[]> = new Observable<any[]>();
constructor(
private firestore: AngularFirestore,
public auth: AngularFireAuth) {}
ngOnInit() {
// Recommended in Firebase documentation
this.auth.onAuthStateChanged((user) => {
if (user) {
this.userDisplayName = user.displayName;
this.leagues$ = this.firestore.collection('Leagues').valueChanges();
} else {
this.userDisplayName = null;
this.leagues$ = new Observable<any[]>();
}
});
}
login() {
this.auth.signInWithPopup(new firebase.auth.GoogleAuthProvider());
}
logout() {
this.auth.signOut();
}
}
app.component.html
<div *ngIf="userDisplayName != null; else showLogin">
<p>Hello {{userDisplayName}}.
<button (click)="logout()">Logout</button>
</p>
<ul>
<li *ngFor="let league of leagues$ | async">
{{ league.name }} - {{ league.location }}
</li>
</ul>
</div>
<ng-template #showLogin>
<p>Please login.</p>
<button (click)="login()">Login</button>
</ng-template>
Resolved by subscribing to the Observable and changing my template to watch an array managed by the subscription. I now get data every time.
Here's the code with the changes:
app.component.ts
...
export class AppComponent implements OnInit {
title = 'Angular Fire Quickstart';
theUser: firebase.User | null = null;
leagueArray: Array<any> = []; // Eliminated the Observable in favor of an Array
constructor(
private firestore: AngularFirestore,
public auth: AngularFireAuth) {}
ngOnInit() {
this.auth.onAuthStateChanged((user) => {
if (user) {
this.theUser = user;
// Added the subscription and populated the array from there.
this.firestore.collection('Leagues').valueChanges().subscribe((data) => {
data.forEach((item) => {
this.leagueArray.push(item);
});
});
} else {
this.theUser = null;
this.leagueArray = [];
}
});
}
...
app.component.html
<div *ngIf="theUser != null">
<p>Hello {{theUser.displayName}}.
<button (click)="logout()">Logout</button>
</p>
<ul>
<!-- Watch the array instead of an Observable -->
<li *ngFor="let league of leagueArray">
{{ league.name }} - {{ league.location }}
</li>
</ul>
</div>
I have rendered ReactComponent with WebComponent but component click event does not trigger
Is there a way to trigger ReactComponent events rendered by WebComponent?
A simple React component
const event = () => {
console.log('aaaaaa');
}
export const Component = ({message}) => {
return (
<button type={'button'} onClick={event}> {message} </button>
)
};
Call it in the WebComponent rendering
export class ConfirmButton extends HTMLElement {
connectedCallback() {
const parent = document.createElement('div');
const message = this.getAttribute('message');
this.attachShadow({ mode: 'open' }).appendChild(parent);
ReactDOM.render(<Component message={message} />, parent);
}
}
By assumption, "aaaa" should be displayed on the console when the button is clicked.
However, no results are displayed ...
react-native-router-flux version: 3.39.1
Hi, i need to access to component state when the user clicks on Back button of navbar to go to previous scene .
Is there a way of doing this ?
i need this to verify the state if the user did some working on any textInput.
https://github.com/aksonov/react-native-router-flux/issues/647
If you use the "cheat" that jo-asakura says, you be able to access component state onBack button :)
// your scene
<Scene key="someKey"
component={ SomeComponent }
onRight={
(state) => {
// state.scene.navigationState.myData is { hello: 'world' }
}
} />
// your component
class SomeComponent extends Component {
// ...
handleButtonClick() {
this.props.navigationState.myData = { hello: 'world' };
}
// ...
}
PS: in my case i have put this line at render function to always save new data.
render(){
this.props.navigationState.myData = this.state;
return()
}
I got this code snippet from Material-ui(Simple example) website itself and it seems it doesn't work immediately by just copying and paste directly.
It throws an error Unexpected token ("line#") while parsing, particularly in the handleChange = ....... . I'm currently using Visual Studio Code and i'm also new to using Material-ui in JSX.
What am i missing? Please help.
import React from 'react';
import DropDownMenu from 'material-ui/lib/DropDownMenu';
import MenuItem from 'material-ui/lib/menus/menu-item';
export default class DropDownMenuSimpleExample extends React.Component {
constructor(props) {
super(props);
this.state = {value: 2};
}
handleChange = (event, index, value) => this.setState({value});
render() {
return (
<DropDownMenu value={this.state.value} onChange={this.handleChange}>
<MenuItem value={1} primaryText="Never"/>
<MenuItem value={2} primaryText="Every Night"/>
<MenuItem value={3} primaryText="Weeknights"/>
<MenuItem value={4} primaryText="Weekends"/>
<MenuItem value={5} primaryText="Weekly"/>
</DropDownMenu>
);
}
}
Change handleChange = (event, index, value) => this.setState({value}); to
handleChange(event, index, value) {
this.setState({value})
};