Vue watch still fires vuex action after updating vuex state - javascript

Hi guys I'm having trouble about my little project, so basically I just need to show to the user the sign up modal if they are not signed in when they click any item on my page. The problem is after they signed in the sign up modal still shows when they click something which should not be since the already signed in.
Here are my codes
SignupModal.vue
axios.get('/user/sign-up', {
params:{
name: this.name,
email: this.email,
password: this.password,
phone: this.phone,
age: this.age,
city: this.city,
}
})
.then( (response) => {
if( response.status == 200 ) {
$('#modals-signup').modal('hide')
this.$store.commit( 'UPDATE_USER_LOGIN_STATE' )
}
});
HomePage.vue
import { mapState } from 'vuex'
export default {
data: function() {
return {
loginState: this.$store.state.isLoggedIn
}
},
computed: {
...mapState([
'isLoggedIn'
]),
},
watch: {
isLoggedIn( newVal, oldVal ) {
this.loginState = newVal
console.log('HOME PAGE WATCH ==================' + newVal + ' ===== ' +this.loginState)
if ( this.loginState ) {
console.log('true since its logged in')
return
}
else {
console.log('false since its logged out')
this.$store.dispatch('preventClickingItems')
}
}
}
}
Store.js
const state = {
isLoggedIn: false,
}
const getters = {
isLoggedIn: (state) => state.isLoggedIn
}
const actions = {
preventClickingItems: ( {commit} ) => {
$('section').on('click', 'div.container *', (e) => {
$('#modals-signup').modal('show')
e.preventDefault()
e.stopPropagation()
return false
})
}
}
const mutations = {
UPDATE_USER_LOGIN_STATE( state ) {
state.isLoggedIn = !state.isLoggedIn
console.log('===========', state.isLoggedIn)
}
}
export default new Vuex.Store({
state,
getters,
actions,
mutations,
plugins: [vuexCookie.plugin]
})
I was able to update the state after user is signed in and able to execute the if block only from the watch, but I dont understand why it still triggers the modal since it only runs the if block.

I have solved this by adding if else statement inside the click event to check the isLoggedIn state value
const actions = {
preventClickingItems: ( {commit, state} ) => {
$('section').on('click', 'div.container *', (e) => {
if ( state.isLoggedIn ) {
return
}
else {
$('#modals-signup').modal('show')
e.preventDefault()
e.stopPropagation()
return false
}
})
}
}

Related

I got navigation as undefined in react navigation 5?

I have a reusable component for Sign in with Apple Button
After user success, i navigate hem to Home screen
But i notes when i log navigation it's log undefined,
and when i log this.props i just got the two actions i made in redux!
So how can i access to navigation in this component and why it's not accessed by default!
Log
props => {"isLogin": [Function isLogin], "storeToken": [Function storeToken]}
navigation => undefined
Code
import appleAuth, {
AppleAuthCredentialState,
AppleAuthError,
AppleAuthRealUserStatus,
AppleAuthRequestOperation,
AppleAuthRequestScope,
AppleButton,
} from '#invertase/react-native-apple-authentication';
import React from 'react';
import {ActivityIndicator, StyleSheet, View} from 'react-native';
import {connect} from 'react-redux';
import API from '../../api/API';
import {isLoginFunc} from '../../redux/actions/isLoginAction';
import {saveToken} from '../../redux/actions/saveTokenAction';
class AppleAuth extends React.Component {
constructor(props) {
super(props);
this.authCredentialListener = null;
this.user = null;
this.state = {
credentialStateForUser: -1,
loading: false,
};
}
componentDidMount() {
const {navigation} = this.props;
console.log('did-navigation', navigation);
console.log('did- this.props', this.props);
/**
* subscribe to credential updates.This returns a function which can be used to remove the event listener
* when the component unmounts.
*/
this.authCredentialListener = appleAuth.onCredentialRevoked(async () => {
// console.warn('Credential Revoked');
this.fetchAndUpdateCredentialState().catch(error =>
this.setState({credentialStateForUser: `Error: ${error.code}`}),
);
});
this.fetchAndUpdateCredentialState()
.then(res => this.setState({credentialStateForUser: res}))
.catch(error =>
this.setState({credentialStateForUser: `Error: ${error.code}`}),
);
}
componentWillUnmount() {
/**
* cleans up event listener
*/
this.authCredentialListener();
}
signIn = async () => {
// start a login request
try {
const appleAuthRequestResponse = await appleAuth.performRequest({
requestedOperation: AppleAuthRequestOperation.LOGIN,
requestedScopes: [
AppleAuthRequestScope.EMAIL,
AppleAuthRequestScope.FULL_NAME,
],
});
this.setState({loading: true});
const {
user: newUser,
email,
nonce,
fullName: {familyName, givenName},
identityToken,
realUserStatus /* etc */,
} = appleAuthRequestResponse;
let username = `${givenName} ${familyName}`;
this.user = newUser;
this.fetchAndUpdateCredentialState()
.then(res => {
this.setState({credentialStateForUser: res});
console.log('res:::', res);
})
.catch(error => {
console.log(`Error: ${error.code}`);
this.setState({credentialStateForUser: `Error: ${error.code}`});
});
if (identityToken) {
console.log('email', email);
console.log('username', username);
console.log('nonce', nonce);
this.sendData(email, username, nonce);
// e.g. sign in with Firebase Auth using `nonce` & `identityToken`
} else {
// no token - failed sign-in?
}
if (realUserStatus === AppleAuthRealUserStatus.LIKELY_REAL) {
console.log("I'm a real person!");
}
// console.warn(`Apple Authentication Completed, ${this.user}, ${email}`);
} catch (error) {
if (error.code === AppleAuthError.CANCELED) {
alert('User canceled Apple Sign in');
// console.warn('User canceled Apple Sign in.');
} else {
console.error(error);
}
}
};
fetchAndUpdateCredentialState = async () => {
if (this.user === null) {
this.setState({credentialStateForUser: 'N/A'});
} else {
const credentialState = await appleAuth.getCredentialStateForUser(
this.user,
);
if (credentialState === AppleAuthCredentialState.AUTHORIZED) {
this.setState({credentialStateForUser: 'AUTHORIZED'});
} else {
this.setState({credentialStateForUser: credentialState});
}
}
};
// Send data "name,image,email" to API
sendData = async (Email, Name, Id) => {
try {
let response = await API.post('/apple', {
email: Email,
name: Name,
id: Id,
});
let {
data: {
data: {
response: {token},
},
},
} = response;
console.log('token:?>:', token);
console.log('props', this.props);
console.log('navigation', this.props.navigation);
this.setState({loading: false});
this.props.storeToken(token);
this.props.isLogin(true);
// this.props.navigation.push('BottomTabNavigator');
} catch (err) {
console.log(err);
alert('Unexpected Error, try again later.');
this.setState({loading: false});
}
};
render() {
return (
<View style={styles.container}>
{this.state.loading ? (
<ActivityIndicator />
) : (
<AppleButton
style={styles.appleButton}
cornerRadius={5}
buttonStyle={AppleButton.Style.WHITE}
buttonType={AppleButton.Type.SIGN_IN}
onPress={() => this.signIn()}
/>
)}
</View>
);
}
}
const styles = StyleSheet.create({
appleButton: {
width: 200,
height: 50,
// margin: 10,
},
container: {
flex: 1,
justifyContent: 'center',
},
});
const mapDispatchToProps = dispatch => {
// to excute the actions we want to invok
return {
isLogin: isLogin => {
dispatch(isLoginFunc(isLogin));
},
storeToken: token => {
dispatch(saveToken(token));
},
};
};
export default connect(
null,
mapDispatchToProps,
)(AppleAuth);
-
singin.js
<AppleAuth /> in the render method
if you render your component as component, not as a navigation screen, it will not receive navigation prop. It was like this in all versions of react-navigation
Access the navigation prop from any component

Redux props logging incorrect data

Right now I am trying to console.log this.streamCreatorUid, but I'm running into a peculiar issue. In my redux debugger, I can clearly see my data in the proper place.
Here is my redux data for the stream creator, directly from my debugger.
streams -
[0] -
{category(pin): "Oldschool Runescape"
displayName(pin): "admin"
streamId(pin): "98ebc719-c7d5-4558-b99d-2d9f8306ec64"
title(pin): "accounttest"
uid(pin): "wsFc7pIMq5dMtw9hPU86DzUTdLO2"
}
I am trying to console.log this.streamCreatorUid from my mapstatetoprops, but it is returning the current user Uid of u9TcrICehNMlAmqyDHQY77L9CXq1 instead. I'm quite confused as to why this is happening, considering this is not the data shown in my debugger.
This is for a personal project. In the past I've accessed redux props like this with no issues, now I'm not quite sure why this is happening.
import React from 'react';
import { database } from '../../../firebaseconfig.js';
import { connect } from 'react-redux';
class StreamFollow extends React.Component {
constructor(props) {
super(props);
this.uid = this.props.uid;
this.displayName = this.props.displayName;
this.streamCreatorUid = this.props.streamCreatorUid;
this.streamCreatorDisplayName = this.props.streamCreatorDisplayName;
}
componentShouldUpdate(prevProps) {
if (this.props.uid !== prevProps.uid) {
this.uid = this.props.uid
}
if (this.props.streamCreatorUid !== prevProps.streamCreatorUid) {
this.streamCreatorUid = this.props.streamCreatorUid;
}
}
//creates a follower object under the stream creators uid
createFollower = (e) => {
const followerRef = database.ref(`User_Follow_Info/${e}/Follower`)
const followerInfoObject = {
uid: this.uid,
displayName: this.displayName
}
followerRef.push(followerInfoObject);
}
//creates a following object under the users uid
//Add in checks to see if following object already exists. We can't follow someone multiple times
createFollowing = (user) => {
const followingRef = database.ref(`User_Follow_Info/${user}/Following`);
const followingInfoObject = {
uid: this.streamCreatorUid,
displayName: this.streamCreatorDisplayName
}
console.log(this.streamCreatorDisplayName)
//Check to see if follow already exists.
/*followingRef.once('value', function (snapshot) {
if (snapshot.hasChild(DATA HERE)) {
alert('exists');
}
}); */
var isSignedIn = this.isSignedIn;
followingRef.orderByChild('uid').equalTo(this.uid).once('value').then(snapshot => {
console.log(snapshot.val());
console.log(this.streamCreatorUid);
if (isSignedIn) {
console.log(snapshot.val())
return
} else {
followingRef.push(followingInfoObject);
}
})
}
onSubmit = () => {
if (this.props.isSignedIn === true) {
this.createFollowing(this.uid);
this.createFollower(this.streamCreatorUid);
} else {
//add in a sign in modal if user is not logged in
console.log('please sign in')
}
}
render() {
return (
<div>
<button onClick={this.onSubmit}>Follow</button>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
isSignedIn: state.auth.isSignedIn,
displayName: state.auth && state.auth.userInfo ? state.auth.userInfo.displayName : null,
uid: state.auth && state.auth.userInfo ? state.auth.userInfo.uid : null,
streamCreatorUid: state.streams && state.streams[0] ? state.streams[0].uid : null,
streamCreatorDisplayName: state.streams && state.streams[0] ? state.streams[0].displayName : null,
}
}
export default connect(mapStateToProps)(StreamFollow);

How to prevent a double click in ReactJS

I am working on search filter in ReactJS and I face a problem. Problem is about when User does some search and wants to click on next (because I have pagination in app) then the User will click twice to load other pages of data.
I need to avoid this behaviour: I mean when user does single click on next it should be "load a data" instead of "double click".
I am new to ReactJS, please expert help me
Code
btnClick() {
const { Item,skip , filtered, data } = this.state
if(filtered.length>0 || !data.length ){
window.alert("Hello,")
this.setState({
filtered:[],
skip:0
},()=>this.getData());
return false
}else {
window.alert("Hello , Dear")
this.setState(
{
Item,
skip: skip + pageSize
},
() => this.getData()
);}
}
You can have a isLoading state and set the disabled prop on the button when in this state which will not allow the button to be clicked again while the data is being fetched.
btnClick() {
const {
Item,
skip,
filtered,
data
} = this.state;
if (filtered.length > 0 || !data.length) {
window.alert("Hello,")
this.setState({
filtered: [],
skip: 0
}, () => this.fetchData());
return false
} else {
window.alert("Hello , Dear")
this.setState({
Item,
skip: skip + pageSize
},
() => this.fetchData()
);
}
}
fetchData() = async() => {
this.setState({ isLoading: true });
await this.getData();
this.setState({ isLoading: false });
}
render() {
const {
isLoading
} = this.state;
const buttonProps = isLoading ? { disabled: true} ? {};
return (
<button onClick={this.btnClick} { ...buttonProps }>
Click to fetch
</button>
);
}

How can I update High Order Component

I created array of routing in ReactJS
const routes = [
{ id: 0, path: '/', view: Home, parent: 0 },
{ id: 1, path: '/a', view: Home2, parent: 0 },
{ id: 2, path: '/b', view: Home3, parent: 1 }
]
Created HOC withAuth which should back to parent routing when user isn't logged. When i going to route (as not logged) - its ok and withAuth back me to parent route, but when i am on route and logout page isn't refresh and I am stay on route for logged users.
import React, { Component } from "react";
import AuthHelper from "./AuthHelper";
export default function withAuth(AuthComponent) {
const Auth = new AuthHelper();
class AuthWrapped extends Component {
constructor(props) {
super(props);
this.state = {
confirm: null,
loaded: false
};
}
checkLogged = () => {
if (!Auth.loggedIn()) {
const parent = this.props.parent;
const obj = this.props.routes
.filter(v => v.id === parent);
this.props.history.replace(obj[0].path);
} else {
try {
const confirm = Auth.getConfirm();
this.setState({
confirm: confirm,
loaded: true
});
} catch (err) {
Auth.logout();
this.props.history.replace("/");
}
}
}
componentDidMount() {
this.checkLogged();
}
render() {
if (this.state.loaded) {
if (this.state.confirm) {
return (
<AuthComponent
history={this.props.history}
confirm={this.state.confirm}
/>
);
} else {
return null;
}
} else {
return null;
}
}
};
return AuthWrapped;
}
I believe that you are using the authentication system the wrong way
In React everything should exist in a hierarchical manner.
In your case, you have an Auth state that would change and when the loggedIn state changes, everything should re-render. the correct way to do this is using the Context API to handle the logged in state so when the state changes, the whole screen would re-render
here is the solution to your problem:
AuthContext.js
const AuthContext = React.createContext();
export class AuthProvider extends React.Component {
state = {
isLoggedIn: false,
};
login = (username, password) => {
someLoginRequestToServer(username, password).then(response => {
this.setState({
isLoggedIn: response.isLoggedIn,
});
});
};
logout = () => {
someLogoutRequestToServer().then(() => {
this.setState({ isLoggedIn: false });
});
};
render() {
return (
<AuthContext.Provider
value={{
loggedIn: this.state.isLoggedIn,
login: this.login,
logout: this.logout,
}}>
{this.props.children}
</AuthContext.Provider>
);
}
}
export const AuthConsumer = AuthContext.Consumer;
SomeCustomAuthComponent
class CustomAuthComponent extends React.Component {
render() {
return (
<AuthConsumer>
{({ loggedIn, login, logout }) => (
<div>
<p>You Are {loggedIn ? 'Logged in' : 'Logged out'}</p>
<button onClick={loggedIn ? () => logout() : () => login('abcd', '12345')} />
</div>
)}
</AuthConsumer>
);
}
}
Or you can use the redux for state management and react-redux as it uses the react Context API under the hood.
hope this helps you! :)
the problem lays here
componentDidMount() {
this.checkLogged();
}
you're checking if the user is logged only when the component is mounted (after the instantiation). you should be checking it every time the page updates, you have to identify a way to handle this mechanism for example by using the componentDidUpdate hook.
export default function withAuth(AuthComponent) {
const Auth = new AuthHelper();
class AuthWrapped extends Component {
constructor(props) {
super(props);
this.state = {
confirm: null,
loaded: false
};
}
checkIsNotLogged = () => {
const parent = this.props.parent;
const obj = this.props.routes
.filter(v => v.id === parent);
this.props.history.replace(obj[0].path);
}
checkLogged = () => {
if (!Auth.loggedIn()) {
this.checkIsNotLogged();
} else {
try {
const confirm = Auth.getConfirm();
this.setState({
confirm: confirm,
loaded: true
});
} catch (err) {
Auth.logout();
this.props.history.replace("/");
}
}
}
componentDidMount() {
this.checkLogged();
}
componentDidUpdate() {
// do not call here the checkLogged method otherwise you could trigger an infinite loop
this.checkIsNotLogged();
}
render() {
if (this.state.loaded) {
if (this.state.confirm) {
return (
<AuthComponent
history={this.props.history}
confirm={this.state.confirm}
/>
);
} else {
return null;
}
} else {
return null;
}
}
};
return AuthWrapped;
}

React setState inside solidity contract instance promises

I deployed a solidity contract to my local testrpc blockchain. All my contract method tests check out, but handling Web3 transactions and updating state accordingly is giving me trouble.
When I add a user account, my next operation is to return all user accounts for my contract. and well...update my state (RegisteredAccounts).
However, through my chain of promises I'm not seeing my states update. I understand setState is asynchronous too, so how can I see my states update without refreshing the page or calling ComponentDidMount()?
Here is my Solidity Accounts Contract (the parts that I've handled so far
pragma solidity ^ 0.4.4;
contract Accounts {
mapping(address => User) public mUsers;
address[] public Users; //users whitepages
struct User {
string handle;
bytes32[] taskList;
}
function addNewUser(string _handle) returns(bool success) {
address newUserAddr = msg.sender;
//if handle not in userAddresses & the handle is not null
if (bytes(mUsers[newUserAddr].handle).length == 0 && bytes(_handle).length != 0) {
mUsers[newUserAddr].handle = _handle;
Users.push(newUserAddr);
return true;
} else {
return false;
}
}
function getUsers() constant returns(address[]) {
return Users;
}
}
Here is my App container component -- relevant parts
registerNewUser() is my problem child right now.
class App extends Component {
state = {
modalOpen: false,
SenderAddress: null,
RegisteredAccounts: [],
isRegisteredUser: false,
SenderTaskList: [], //not set
AccountsCtrct: null,
web3: null
}
//#region APP METHODS
componentWillMount() {
// Get network provider and web3 instance. -- See utils/getWeb3 for more info.
getWeb3.then(results => {
this.setState({
web3: results.web3
})
this.instantiateContracts() //instantiate contract
}).catch(() => {
console.log('Error finding web3.')
})
}
instantiateContracts() {
this.setState({
AccountsCtrct: contract(AccountsContract)
})
this.state.AccountsCtrct.setProvider(this.state.web3.currentProvider)
//Get block chain addresses --- only returns the current address selected in metamask (web3 current addr)
this.state.web3.eth.getAccounts((error, accounts) => {
this.setState({
SenderAddress: accounts[0]
})
//INIT ACCOUNTS CONTRACT
var acctDeployed = this.state.AccountsCtrct.deployed()
acctDeployed.then((instance) => {
return instance.getUsers();
}).then((res) => {
this.setState({
RegisteredAccounts: res
})
if (this.state.RegisteredAccounts.includes(this.state.SenderAddress)) {
this.setState({
isRegisteredUser: true
})
}
})
})
}
registerUser = (handle) => {
var acctInstance
this.state.AccountsCtrct.deployed().then((inst) => {
//add current user to this account
acctInstance = inst
return acctInstance.addNewUser(handle, {
from: this.state.SenderAddress
});
}).then(() => {
//now we added our user -- update registeredAccounts setState
//pass response users array to promise
return acctInstance.getUsers()
}).then(res => {
this.setState({
RegisteredAccounts: res
})
if (res.includes(this.state.SenderAddress)) {
this.setState({
isRegisteredUser: true
})
}
})
}
toggleModal = () => {
this.setState(prevState => ({
modalOpen: !prevState.modalOpen
}));
}
//#endregion
render() {
return (
<div className="App">
<nav className="navbar pure-menu pure-menu-horizontal">
Truffle Box
{
!this.state.isRegisteredUser
? <a style={navLink} onClick={ this.toggleModal } href="#" className="pure-menu-heading pure-menu-link">Register</a>
: null
}
</nav>
<ModalUserNav visible={this.state.modalOpen}
toggleModal={this.toggleModal}
isRegistered={this.state.isRegisteredUser}
registerUser={this.registerUser} />
);
}
}
Last my Child component
class ModalUserNav extends Component {
state = {
unpl: "UserName",
pwpl: "Password",
errorCode: 'Registration Failed',
errorVisible: false
}
handleOnChangePL = (e) => {
this.setState({
[e.target.name]: e.target.value
})
}
handleSubmit = () => {
if (this.state.unpl !== "") {
this.props.registerUser(this.state.unpl)
this.props.toggleModal();
} else {
//if the input is empty update the error code and show
console.log('registration failed!')
this.setState({
errorCode: 'REGISTRATION ERR: empty handles are not allowed!',
errorVisible: true
})
}
}
render() {
return (
<section>
<Modal visible={this.props.visible} effect="fadeInUp">
<div className="pure-form">
<fieldset style={modalFormView}>
<legend style={{fontSize: "18px"}}><b>Register now. All you need is a handle!</b></legend>
<div className="flexContainer">
<input style={{marginTop: "7px", height: "2.6em", marginLeft: "5px", marginRight: "5px"}} type="text" name="unpl" placeholder={this.state.unpl} onChange={(event) => {this.handleOnChangePL(event)}} value={this.state.unpl} />
<button style={btnStyle} type="submit" className="pure-button pure-button-primary" onClick={() => {this.handleSubmit()}}><b>Register</b></button>
</div>
</fieldset>
</div>
</Modal>
</section>
)
}
}
In short, I want to follow up my 2 asynchronous tasks (addNewUser, getUsers) with a setState so I can automatically change my UI without refreshing. So what am I doing wrong?
You should move instantiateContracts to setState because setState does not update data immediately. https://reactjs.org/docs/react-component.html#setstate
this.setState({
web3: results.web3
}, () => {
this.instantiateContracts() //instantiate contract
})
Update 1: About registerUser: It should be
this.setState({
RegisteredAccounts: res
}, () => {
if (res.includes(this.state.SenderAddress)) {
this.setState({
isRegisteredUser: true
})
}
})

Categories

Resources