I'm developing a React native application. I'm using the React native elements library.
I'm using the Search bar. But when I type faster than the keyboard, the search is not working properly.
Example;
I'm writing "Jack," but it's called "Ja".
I hope I can explain my problem. Because my English is not very good. Thanks in advance for your help.
handleRefresh = () => {
this.setState({
offset: 0,
maxSize: 10,
isSearch: false
}, () => {
this.loadData();
});
};
handleLoadMore = () => {
this.setState({
maxSize: this.state.maxSize + 10
}, () => {
this.loadData();
});
};
loadData = async () => {
try {
const { username, token, offset, maxSize } = this.state;
var credentials = Base64.btoa(username + ':' + token);
var URL = `https://crm.example.com/api/v1/Lead?select=name,status&sortBy=createdAt&asc=false&offset=${offset}&maxSize=${maxSize}`;
await axios.get(URL, {headers : { 'Espo-Authorization' : credentials }})
.then(this.dataSuccess.bind(this))
.catch(this.dataFail.bind(this));
}catch (error) {
Alert.alert(
'Hata',
'Bir hata meydana geldi. Lütfen yöneticiye başvurunuz.',
[
{ text: 'Tamam', onPress: () => null }
]
);
}
};
searchLead = async (text) => {
try {
if(text) {
this.setState({ searchText: text, isSearch: true, isLoading: true });
const { username, token, maxSize } = this.state;
var credentials = Base64.btoa(username + ':' + token);
var URL = "https://crm.example.com/api/v1/Lead?select=name,status&sortBy=createdAt&asc=false&where[0][type]=textFilter&where[0][value]=" + text;
await axios.get(URL, { headers : { 'Espo-Authorization' : credentials }})
.then(this.dataSearch.bind(this))
.catch(this.dataFail.bind(this));
}else {
this.setState({ searchText: '' });
this.handleRefresh();
}
}catch (error) {
Alert.alert(
'Hata',
'Arama başarısız oldu. Lütfen yöneticiniz ile görüşün.',
[
{ text: 'Tamam', onPress: () => null }
]
);
}
}
dataSuccess(response) {
this.setState({ isLoading: false, leadList: response.data.list });
}
dataSearch(response) {
this.setState({ isLoading: false, searchData: response.data.list });
}
dataFail(error) {
this.setState({ isLoading: false });
Alert.alert(
'Hata',
'Beklenmedik bir hata oluştu',
[
{ text: 'Tamam', onPress: () => null }
]
);
}
render() {
const { isLoading, isRefreshing, searchText, isSearch, leadList, searchData } = this.state;
return(
<View style={styles.container}>
<SearchBar
placeholder="Bir lead arayın..."
searchIcon={<Icon
name="search"
color="white"
size={21}
/>}
onChangeText={this.searchLead.bind(this)}
onClear={this.handleRefresh.bind(this)}
onCancel={this.handleRefresh.bind(this)}
value={searchText}
/>
{ isLoading ? <ActivityIndicator style={styles.loading} size="large" color="orange" /> :
isSearch ?
<ScrollView>
<FlatList
data={searchData}
showLoading={true}
renderItem={({item}) =>
<ListItem
title={item.name}
subtitle={item.status}
bottomDivider={true}
/>
}
keyExtractor={this.keyExtractor}
refreshing={isRefreshing}
onRefresh={this.handleRefresh}
/>
</ScrollView> :
<FlatList
data={leadList}
renderItem={({item}) =>
<ListItem
title={item.name}
subtitle={item.status}
bottomDivider={true}
/>
}
keyExtractor={this.keyExtractor}
refreshing={isRefreshing}
onRefresh={this.handleRefresh}
onEndReached={this.handleLoadMore}
onEndReachedThreshold={0.2}
/>
}
</View>
)
}
}
Let me start by saying I don't know reactjs very well, but I think this may work (there may be far better solutions though)
searchLead = (() => {
let req;
const delayedRequest = callback => {
const timeout = setTimeout(callback, 500);
return {
cancel() {
clearTimeout(timeout);
}
};
};
return (text) => {
req && req.cancel();
req = delayedRequest(async () => {
try {
if(text) {
this.setState({ searchText: text, isSearch: true, isLoading: true });
const { username, token, maxSize } = this.state;
var credentials = Base64.btoa(username + ':' + token);
var URL = "https://crm.example.com/api/v1/Lead?select=name,status&sortBy=createdAt&asc=false&where[0][type]=textFilter&where[0][value]=" + text;
await axios.get(URL, { headers : { 'Espo-Authorization' : credentials }})
.then(this.dataSearch.bind(this))
.catch(this.dataFail.bind(this));
} else {
this.setState({ searchText: '' });
this.handleRefresh();
}
}catch (error) {
Alert.alert(
'Hata',
'Arama başarısız oldu. Lütfen yöneticiniz ile görüşün.',
[
{ text: 'Tamam', onPress: () => null }
]
);
}
});
};
})();
Related
I have a problem with the following exportHtml function, this must export the html content of a react component but I can't understand why it doesn't enter the function to extrapolate html, how can I solve it?
Editor.js
import EmailEditor.js
const Example = () => {
const emailEditorRef = useRef(null);
const [titolo, setTitolo] = useState('');
const [getHtml, setHtml] = useState('');
const [newsProfileValue, setNewsProfileSet] = useState('');
const [profileList, setNewsProfileList] = useState([]);
const [premiumValue, setPremium] = useState(false);
const [publicValue, setPublic] = useState(false);
const arr = [];
const newsCollectionRef = collection(db, 'news');
const saveDesign = () => {
emailEditorRef.current.editor.saveDesign((design) => {
exportData(design);
});
};
const getList = async () => {
try {
var id = await localStorage.getItem('uid');
console.log(id);
var data = [];
const querySnapshot = await getDocs(collection(db, 'news_profiles'));
querySnapshot.forEach((doc) => {
if (doc.data().about.uid == id) {
data.push({ title: doc.data().about.title, id: doc.id });
arr.push({ doc: doc.data().about.title, id: doc.id });
}
});
} catch (error) {
console.log('Error: ' + error);
}
var options = arr.map((e) => {
return `<option value="${e.id}">${e.doc}</option>`;
});
document.getElementById('selectNumber').innerHTML = options;
setNewsProfileList(data);
};
const exportData = (design) => {
const jsonString = `data:text/json;chatset=utf-8,${encodeURIComponent(
JSON.stringify(design)
)}`;
const link = document.createElement('a');
link.href = jsonString;
link.download = 'data.json';
link.click();
};
const exportHtml = () => {
console.log('Sono in html');
emailEditorRef.current.editor.exportHtml((data) => {
const { design, html } = data;
setHtml(html);
});
addElementToFireStoreNews(getHtml);
};
const addElementToFireStoreNews = async (html) => {
try {
console.log('Sono in addElementToFireStoreNews ');
var uuid = await localStorage.getItem('uid');
var jsonFile;
var templateid = await localStorage.getItem('templateid');
if (templateid == 1) {
jsonFile = sample;
} else if (templateid == 2) {
jsonFile = sample3;
} else {
jsonFile = sample2;
}
console.log('Setup options');
const options = {
method: 'POST',
url: 'http://link',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer kjsdgu6523kjbnap!w',
},
data: {
internal: true,
online: publicValue,
premium: premiumValue,
html: html,
template: jsonFile,
subject: html,
email: uuid,
},
};
console.log('Chiamo il rest');
axios
.request(options)
.then(function (response) {
console.log('data: ' + response.data);
})
.catch(function (error) {
console.error('Errore axios: ' + error);
});
} catch (err) {
console.log('Errore inserimnento: ' + err);
}
};
const onDesignLoad = (data) => {
getList();
console.log('onDesignLoad', data);
};
const onLoad = async () => {
var templateid = await localStorage.getItem('templateid');
if (templateid == 1) {
emailEditorRef.current.editor.loadDesign(sample);
} else if (templateid == 2) {
emailEditorRef.current.editor.loadDesign(sample3);
} else {
emailEditorRef.current.editor.loadDesign(sample2);
}
emailEditorRef.current.editor.addEventListener(
'design:loaded',
onDesignLoad
);
};
const onReady = () => {};
const changeText = (text) => {
setTitolo(text);
};
const clickValue = (e) => {
setNewsProfileSet(e.target.value);
};
const changeValue = () => {
setPremium(!premium);
};
const changeValuePublic = () => {
setPublic(!publicValue);
};
return (
<Container>
<Bar>
<img src="firebase.png" width="100" height="50" />
<label style={{ color: 'white' }}>
<b>Titolo News: </b>
<input
type="text"
name="name"
onChange={(value) => changeText(value.target.value)}
style={{ marginRight: 200 }}
/>
Premium:{' '}
<Checkbox
{...label}
unchecked
onClick={() => changeValue()}
style={{ color: 'white' }}
/>
Online:{' '}
<Checkbox
{...label}
unchecked
onClick={() => changeValuePublic()}
style={{ color: 'white' }}
/>
</label>
<form id="myForm">
<select id="selectNumber" onClick={clickValue}>
<option>Scegli il news profile</option>
</select>
<button onClick={() => saveDesign}> Salva Design </button>
<button onClick={() => exportHtml()}> Salva Articolo </button>{' '}
</form>
</Bar>
<React.StrictMode>
<EmailEditor ref={emailEditorRef} onLoad={onLoad} onReady={onReady} />{' '}
</React.StrictMode>{' '}
</Container>
);
};
export default Example;
EmailEditor.js
import React, { Component } from 'react';
import { loadScript } from './loadScript';
import pkg from '../package.json';
let lastEditorId = 0;
export default class extends Component {
constructor(props) {
super(props);
this.editorId = props.editorId || `editor-${++lastEditorId}`;
}
componentDidMount() {
loadScript(this.loadEditor, this.props.scriptUrl);
}
render() {
let {
props: { minHeight = 500, style = {} },
} = this;
return (
<div
style={{
flex: 1,
display: 'flex',
minHeight: minHeight,
}}
>
<div id={this.editorId} style={{ ...style, flex: 1 }} />
</div>
);
}
loadEditor = () => {
if (this.loaded) return;
this.loaded = true;
const options = this.props.options || {};
if (this.props.projectId) {
options.projectId = this.props.projectId;
}
if (this.props.tools) {
options.tools = this.props.tools;
}
if (this.props.appearance) {
options.appearance = this.props.appearance;
}
if (this.props.locale) {
options.locale = this.props.locale;
}
this.editor = unlayer.createEditor({
...options,
id: this.editorId,
displayMode: 'email',
source: {
name: pkg.name,
version: pkg.version,
},
});
for (const [key, value] of Object.entries(this.props)) {
if (/^on/.test(key) && key !== 'onLoad' && key !== 'onReady') {
this.addEventListener(key, value);
}
}
const { onLoad, onReady } = this.props;
// #deprecated
onLoad && onLoad();
if (onReady) this.editor.addEventListener('editor:ready', onReady);
};
registerCallback = (type, callback) => {
this.editor.registerCallback(type, callback);
};
addEventListener = (type, callback) => {
this.editor.addEventListener(type, callback);
};
loadDesign = (design) => {
this.editor.loadDesign(design);
};
saveDesign = (callback) => {
this.editor.saveDesign(callback);
};
exportHtml = (callback) => {
this.editor.exportHtml(callback);
};
setMergeTags = (mergeTags) => {
this.editor.setMergeTags(mergeTags);
};
}
Im tryin to get my queries to update using the subscribeToMore function. Im getting lost on why the code is a failing to execute. so far all operations query, mutation and subscribe will work independently.
FRONTEND
export const QUERY_MESSAGES = gql`
query Messages($convoId: ID!) {
messages(convoId: $convoId) {
text
senderId
convoId
createdAt
}
}
`;
export const SUBSCRIBE_MESSAGES = gql`
subscription Messages($convoId: ID!) {
messages(convoID: $convoID) {
text
convoId
}
}
`;
const ActiveChat = ({ currentConvo, me }: any) => {
const { subscribeToMore, data } = useQuery(QUERY_MESSAGES, {
variables: {
convoId: currentConvo,
},
});
return (
<section className="chat-wrapper">
<div className="messages-container">
<Messages
messages={data.messages}
me={me}
subscribeToMessages={() => {
subscribeToMore({
document: SUBSCRIBE_MESSAGES,
variables: { convoId: currentConvo },
updateQuery: (prev, { subscriptionData }) => {
console.log("hit");
if (!subscriptionData.data) return prev;
const newFeedItem = subscriptionData.data.messages;
return Object.assign({}, prev, {
messages: [newFeedItem, ...prev.messages],
});
},
});
}}
/>
<Input currentConvo={currentConvo} />
</div>
</section>
);
};
const Messages = ({ messages, me, subscribeToMessages }: any) => {
useEffect(() => {
subscribeToMessages();
console.log("use effect ran");
});
const formatTime = (time: number) =>
new Date(time * 1000).toLocaleTimeString();
console.log(messages);
return (
messages && (
<div>
{messages.map((message: any, i: number) => {
return message.senderId === me?._id ? (
<SenderBubble
key={i}
text={message.text}
time={message.createdAt}
formatTime={formatTime}
/>
) : (
<OtherUserBubble
key={i}
text={message.text}
time={message.createdAt}
formatTime={formatTime}
/>
);
})}
</div>
)
);
};
BACKEND
const pubsub = new PubSub();
Query: {
messages: async (parent, { convoId }, context) => {
if (context.user) {
return Message.find({ convoId }).sort({ createdAt: "asc" });
}
},
},
Mutation: {
/// creates a new message using sender id and emits MESSAGE_SENT event
sendMessage: async (parent, { text, convoId }, context) => {
pubsub.publish("MESSAGE_SENT", {
message: { text, convoId },
});
return Message.create({ text, convoId, senderId: context.user._id });
},
},
Subscription: {
userActive: {
subscribe: () => pubsub.asyncIterator("ACTIVE_USERS"),
},
message: {
subscribe: withFilter(
() => pubsub.asyncIterator("MESSAGE_SENT"),
(payload, variables) => {
return payload.message.convoId === variables.convoId;
}
),
},
},
tdlr: I think subscribeToMore.updateQuery isn't firing but i don't how to debug
SOLVED: turns out I placed the wrong url and port number while building the client so the ws wasn't connecting.
So I'm quite new to React native and I am making updates to an existing app. I have two dropdowns: a country dropdown and a city dropdown (These were originally text fields). When the country is set, the cities dropdown is populated from a JSON file. All this works okay, except when you change country the cities list is not populated immediately. If you set country twice - it sets the cities of the previous selection.
This is the relevant code in the page component:
[imports all in place]
const countries2 = require('../../common/constants/countries2.json');
const cities = require('../../common/constants/cities.json');
const validator = {
city: {
required: true,
string: true,
},
country: {
required: true,
string: true,
},
};
const RegistrationAddress = ({ route }) => {
const navigation = useNavigation();
const { personalDetails, socialRegistration } = route.params || {};
const [updateUser] = useMutation(UPDATE_PROFILE, {
fetchPolicy: 'no-cache',
});
const [state, setState] = useState({
city: '',
country: '',
personalDetails: personalDetails,
});
const [errors, setErrors] = useState({});
const [filteredCities, setCities] = useState([]);
const onChange = (field, val) => {
if (field === 'country') {
updateCityPicker();
}
const newState = { ...state, [field]: val };
setState(newState);
const err = validate(newState, validator);
if (err) {
setErrors(err);
} else {
setErrors({});
}
};
useEffect(() => {
const err = validate({}, validator);
setErrors(err);
getUserAddressDetails();
}, []);
const getUserAddressDetails = async () => {
const userAddressDetail = await AsyncStorage.getItem('userAddressDetails');
const userAddressDetails = JSON.parse(userAddressDetail);
const userAddressDetailsState = {
...state,
city: userAddressDetails.city || '',
country: userAddressDetails.country || '',
};
setState(userAddressDetailsState);
const err = validate(userAddressDetailsState, validator);
if (err) {
setErrors(err);
} else {
setErrors({});
}
};
const updateCityPicker = () => {
const suggestions = cities.filter((el) => {
var s = el[country];
return s;
});
var list = [];
suggestions.forEach((element) => {
element[country].forEach((cl) => {
var obj = {};
obj['label'] = cl;
obj['value'] = cl;
list.push(obj);
});
});
setCities(list);
};
const { city, country } = state;
const navigateToRegistrationConfirmDetails = () => {
if (socialRegistration) {
updateUser({
variables: {
data: {
city,
country,
},
},
}).then(async () => {
await AsyncStorage.removeItem('userBasicDetails');
await AsyncStorage.removeItem('userAddressDetails');
navigation.navigate('QuestionnaireIntroduction');
});
} else {
navigation.navigate('RegistrationConfirmDetails', { userDetails: state });
}
};
return (
<ImageBackground style={styles.imageBackground}>
<View style={styles.regStepView}>
<Text style={styles.regStep}>
Address
<Text style={styles.oneOutOfThree}> - 3/3</Text>
</Text>
</View>
<ScrollView showsVerticalScrollIndicator={false}>
<KeyboardAvoidingView style={styles.inputsView}>
<Dropdown
mainContainerStyle={{
width: '100%',
backgroundColor: 'white',
marginTop: 5,
}}
textStyle={{ fontSize: verticalScale(14) }}
value={country}
onValueChange={(val) => onChange('country', val)}
testID="countryID"
placeholder="eg. United Kingdom"
items={countries2}
checkDropdownErrors={false}
error={errors.accountCurrency && errors.accountCurrency[0]}
showDropdownError={''}
title="Which country do you currently live in?"
/>
<Dropdown
mainContainerStyle={{
width: '100%',
backgroundColor: 'white',
marginTop: 5,
}}
textStyle={{ fontSize: verticalScale(14) }}
value={city}
onValueChange={(val) => onChange('city', val)}
testID="cityID"
placeholder="eg. London"
items={filteredCities}
checkDropdownErrors={false}
error={errors.city && errors.city[0]}
showDropdownError={''}
title="Which city do you currently live in?"
/>
</KeyboardAvoidingView>
</ScrollView>
<View>
<Button
disabled={Object.keys(errors).length}
styleContainer={{ marginBottom: scale(24) }}
title="Next"
onPressFunc={async () => {
await AsyncStorage.setItem(
'userAddressDetails',
JSON.stringify(state),
)
.then(() => navigateToRegistrationConfirmDetails())
.catch((err) => console.log({ err }));
}}
/>
</View>
</ImageBackground>
);
};
export default RegistrationAddress;
I also get a warning about setting the state of a component from within another component, which is understandable, but I don't know the solution.
Any help would be hugely appreciated. Sorry if it's an existing question - other answers didn't quite work for me.
Problem is setState is async. Its order is not guaranteed. So in your case its good to keep single useState as you are already using dictionary. Idea is that when you change country cityList will be filtered according to country else its empty. I have updated code as below, try it. It should work for you.
Explanation: I have introduced new cityList attribute which will be updated in case of country is changed. Also to display dropdown list we use the same cityList attribute for city dropdown.
[imports all in place]
const countries2 = require('../../common/constants/countries2.json');
const cities = require('../../common/constants/cities.json');
const validator = {
city: {
required: true,
string: true,
},
country: {
required: true,
string: true,
},
};
const RegistrationAddress = ({ route }) => {
const navigation = useNavigation();
const { personalDetails, socialRegistration } = route.params || {};
const [updateUser] = useMutation(UPDATE_PROFILE, {
fetchPolicy: 'no-cache',
});
const [state, setState] = useState({
city: '',
country: '',
cityList: [],
personalDetails: personalDetails,
});
const [errors, setErrors] = useState({});
const onChange = (field, val) => {
let cityList = state.cityList;
if (field === 'country') {
cityList = cities.filter((el) => {
var s = el[country];
return s;
});
}
const newState = { ...state, [field]: val, cityList };
setState(newState);
const err = validate(newState, validator);
if (err) {
setErrors(err);
} else {
setErrors({});
}
};
useEffect(() => {
const err = validate({}, validator);
setErrors(err);
getUserAddressDetails();
}, []);
const getUserAddressDetails = async () => {
const userAddressDetail = await AsyncStorage.getItem('userAddressDetails');
const userAddressDetails = JSON.parse(userAddressDetail);
const userAddressDetailsState = {
...state,
city: userAddressDetails.city || '',
country: userAddressDetails.country || '',
};
setState(userAddressDetailsState);
const err = validate(userAddressDetailsState, validator);
if (err) {
setErrors(err);
} else {
setErrors({});
}
};
const updateCityPicker = () => {
const suggestions = cities.filter((el) => {
var s = el[country];
return s;
});
var list = [];
suggestions.forEach((element) => {
element[country].forEach((cl) => {
var obj = {};
obj['label'] = cl;
obj['value'] = cl;
list.push(obj);
});
});
setCities(list);
};
const { city, country, cityList } = state;
const navigateToRegistrationConfirmDetails = () => {
if (socialRegistration) {
updateUser({
variables: {
data: {
city,
country,
},
},
}).then(async () => {
await AsyncStorage.removeItem('userBasicDetails');
await AsyncStorage.removeItem('userAddressDetails');
navigation.navigate('QuestionnaireIntroduction');
});
} else {
navigation.navigate('RegistrationConfirmDetails', { userDetails: state });
}
};
return (
<ImageBackground style={styles.imageBackground}>
<View style={styles.regStepView}>
<Text style={styles.regStep}>
Address
<Text style={styles.oneOutOfThree}> - 3/3</Text>
</Text>
</View>
<ScrollView showsVerticalScrollIndicator={false}>
<KeyboardAvoidingView style={styles.inputsView}>
<Dropdown
mainContainerStyle={{
width: '100%',
backgroundColor: 'white',
marginTop: 5,
}}
textStyle={{ fontSize: verticalScale(14) }}
value={country}
onValueChange={(val) => onChange('country', val)}
testID="countryID"
placeholder="eg. United Kingdom"
items={countries2}
checkDropdownErrors={false}
error={errors.accountCurrency && errors.accountCurrency[0]}
showDropdownError={''}
title="Which country do you currently live in?"
/>
<Dropdown
mainContainerStyle={{
width: '100%',
backgroundColor: 'white',
marginTop: 5,
}}
textStyle={{ fontSize: verticalScale(14) }}
value={city}
onValueChange={(val) => onChange('city', val)}
testID="cityID"
placeholder="eg. London"
items={cityList}
checkDropdownErrors={false}
error={errors.city && errors.city[0]}
showDropdownError={''}
title="Which city do you currently live in?"
/>
</KeyboardAvoidingView>
</ScrollView>
<View>
<Button
disabled={Object.keys(errors).length}
styleContainer={{ marginBottom: scale(24) }}
title="Next"
onPressFunc={async () => {
await AsyncStorage.setItem(
'userAddressDetails',
JSON.stringify(state),
)
.then(() => navigateToRegistrationConfirmDetails())
.catch((err) => console.log({ err }));
}}
/>
</View>
</ImageBackground>
);
};
export default RegistrationAddress;
CodeSandbox: https://codesandbox.io/s/stupefied-heisenberg-fery5
HeaderMenu.js
import {Component} from 'react';
import {
Menu,
Container,
Button,
Label,
Loader,
List,
Image,
Icon,
Dropdown
} from 'semantic-ui-react';
import Head from 'next/head';
import web3 from '../ethereum/web3';
import Constant from '../support/Constant';
import Config from '../support/Config';
import appDispatcher from '../core/AppDispatcher';
import contractManager from '../core/ContractManager';
class HeaderMenu extends Component {
constructor(props) {
super(props);
this.account = props.account;
this.contractManager = contractManager;
// console.log(contractManager);
this.transactionDispatcher = props.transactionDispatcher;
this.state = {address: "", balance: "", name: "",
avatarUrl: "", isLoading: true, isJoinButtonLoading: false,
isJoined: false, numPendingTx: 0};
this.reloadCount = 0;
}
clearAllData = () => {
window.localStorage.clear();
}
componentDidMount() {
if (this.account) {
this.getAccountInfo();
appDispatcher.register((payload) => {
if (payload.action == Constant.EVENT.ACCOUNT_BALANCE_UPDATED) {
this.setState({balance: this.account.balance});
} else if (payload.action == Constant.EVENT.ACCOUNT_INFO_UPDATED) {
this.setState({name: payload.profile.name, avatarUrl: payload.profile.avatarUrl, isJoined: payload.profile.isJoined});
}
});
this.transactionDispatcher.register((payload) => {
if (payload.action == Constant.EVENT.PENDING_TRANSACTION_UPDATED) {
this.setState({numPendingTx: payload.numPendingTx});
}
});
}
}
getAccountInfo = () => {
var address = this.account.getAddress();
if (address) {
this.setState({address: address, balance: this.account.balance, isLoading: false, isJoined: this.account.isJoined});
} else {
if (this.reloadCount == 1) {
this.setState({isLoading: false});
} else {
this.reloadCount++;
setTimeout(this.getAccountInfo, 800);
}
}
}
handleDropdownClicked = (event, data) => {
if (data.name == 'updateProfile') {
appDispatcher.dispatch({
action: Constant.ACTION.OPEN_UPDATE_PROFILE
});
} else if (data.name == 'logOutItem') {
this.clearAllData();
window.location.reload();
} else if (data.name == 'settingsItem') {
appDispatcher.dispatch({
action: Constant.ACTION.OPEN_SETTINGS_MODAL
})
}
else if (data.name == 'changeEthNetwork') {
if (data.networkid != Config.ENV.EthNetworkId) {
Config.ENV.EthNetworkId = data.networkid;
this.removeNetworkDependentData();
window.location.reload();
}
}
}
removeNetworkDependentData = () => {
this.account.storageManager.removeNetworkDependentData();
}
handleJoinClicked = () => {
var publicKeyBuffer = this.account.getPublicKeyBuffer();
this.contractManager.joinContract(publicKeyBuffer, (resultEvent) => {
if (resultEvent == Constant.EVENT.ON_REJECTED || resultEvent == Constant.EVENT.ON_ERROR) {
this.setState({isJoinButtonLoading: false});
} else if (resultEvent == Constant.EVENT.ON_RECEIPT) {
window.location.reload();
}
});
this.setState({isJoinButtonLoading: true});
}
handleImportPrivateKeyClicked = () => {
appDispatcher.dispatch({
action: Constant.ACTION.OPEN_PRIVATE_KEY_MODAL
});
}
render() {
var accountInfo = (<div></div>);
if (this.account) {
if (this.state.isLoading == false) {
if (this.state.address) {
var addressExplorerUrl = Config.ENV.ExplorerUrl + 'address/' + this.state.address;
var dropdownTrigger;
if (this.state.avatarUrl) {
dropdownTrigger = (
<span><Image src={this.state.avatarUrl} avatar/>{ this.state.name ? this.state.name : this.state.address.substr(0,10)}</span>
);
} else {
dropdownTrigger = (
<span><Icon name='user' size='large'/>{ this.state.name ? this.state.name : this.state.address.substr(0,10)}</span>
);
}
var networkItems = [];
for (var i=0;i<Config.NETWORK_LIST.length;i++) {
networkItems.push(
<Dropdown.Item key={'networkItem' + i} networkid={Config.NETWORK_LIST[i].id} name='changeEthNetwork' onClick={this.handleDropdownClicked}>
{Config.NETWORK_LIST[i].name}
</Dropdown.Item>
);
}
var memberInfo;
if (this.account.isJoined) {
memberInfo = (
<Dropdown item trigger={dropdownTrigger}>
<Dropdown.Menu>
<Dropdown.Item name='updateProfile' onClick={this.handleDropdownClicked}>
<Icon name='write'/>Update profile
</Dropdown.Item>
<Dropdown.Item name='settingsItem' onClick={this.handleDropdownClicked}>
<Icon name='settings'/>Settings
</Dropdown.Item>
<Dropdown.Item name='logOutItem' onClick={this.handleDropdownClicked}>
<Icon name='log out'/>Log out
</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
);
} else {
memberInfo = (
<Button color='orange' onClick={this.handleJoinClicked}
loading={this.state.isJoinButtonLoading}
disabled={this.state.isJoinButtonLoading}>Join {Constant.APP_NAME}</Button>
);
}
var pendingTxItem;
if (this.state.numPendingTx > 0) {
pendingTxItem = (
<Label as='a' color='yellow' href={addressExplorerUrl} target='_blank'>
<Icon name='spinner' loading/>
{this.state.numPendingTx} pending tx
</Label>
);
}
accountInfo = (
<Menu.Menu position='right'>
<Menu.Item>
<Dropdown item text={Config.ENV.NetworkName}>
<Dropdown.Menu>
{networkItems}
</Dropdown.Menu>
</Dropdown>
</Menu.Item>
<Menu.Item>
<List>
<List.Item>
<a href={addressExplorerUrl} target='_blank'>
{this.state.address}
</a>
</List.Item>
<List.Item>
Balance: <Label as='a' href={addressExplorerUrl} target='_blank' color='orange'>{parseFloat(web3.utils.fromWei("" +this.state.balance, 'ether')).toFixed(8) + ' ETH' }</Label>
{pendingTxItem}
</List.Item>
</List>
</Menu.Item>
<Menu.Item>
{memberInfo}
</Menu.Item>
</Menu.Menu>
);
} else {
accountInfo = (
<Menu.Menu position='right'>
<Menu.Item>
<Button onClick={this.handleImportPrivateKeyClicked} color='blue'>Import private key</Button>
</Menu.Item>
</Menu.Menu>
);
}
} else {
accountInfo = (<Loader inverted active />);
}
}
return (
<Menu fixed='top' color='grey' inverted>
<Head>
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.2.12/semantic.min.css"></link>
</Head>
<Container>
<Menu.Item>
<a href='/'><Image src='static/images/blockchat.png' height={55} /></a>
</Menu.Item>
{this.account ? accountInfo: (<div></div>)}
</Container>
</Menu>
);
}
}
export default HeaderMenu;
ContractManager.js
import web3 from '../ethereum/web3';
import compiledContract from '../ethereum/build/EtherChat.json';
import TransactionsManager from './TransactionManager';
import appDispatcher from './AppDispatcher';
import Config from '../support/Config';
import Constant from '../support/Constant';
import utils from '../support/Utils';
import crypto from 'crypto';
/**
* Responsible for interacting with the Ethereum smart contract
*/
export class ContractManager {
constructor(accountManager, storageManager) {
this.getContract();
this.accountManager = accountManager;
this.storageManager = storageManager;
this.transactionManager = new TransactionsManager(accountManager);
}
// Create a web3 contract object that represent the ethereum smart contract
getContract = async () => {
this.contract = await new web3.eth.Contract(JSON.parse(compiledContract.interface),
Config.ENV.ContractAddress);
appDispatcher.dispatch({
action: Constant.EVENT.CONTRACT_READY
})
}
// Get current account profile from EtherChat contract's storage
getProfile = async (address) => {
var result = await this.contract.methods.members(this.accountManager.getAddress()).call();
var profile = {};
if (result.isMember == 1) {
profile.isJoined = true;
profile.avatarUrl = utils.hexStringToAsciiString(result.avatarUrl);
profile.name = utils.hexStringToAsciiString(result.name);
this.storageManager.setJoinedStatus(true);
this.storageManager.setName(this.name);
this.storageManager.setAvatarUrl(this.avatarUrl);
appDispatcher.dispatch({
action: Constant.EVENT.ACCOUNT_INFO_UPDATED,
profile: profile
})
}
return profile;
}
getMemberInfo = async (address, relationship) => {
var memberInfo = await this.contract.methods.members(address).call();
if (memberInfo.isMember) {
var publicKey = '04' + memberInfo.publicKeyLeft.substr(2) + memberInfo.publicKeyRight.substr(2);
var name = utils.hexStringToAsciiString(memberInfo.name);
var avatarUrl = utils.hexStringToAsciiString(memberInfo.avatarUrl);
this.storageManager.updateContact(address, publicKey, name, avatarUrl, relationship);
}
}
getPastEvents = async (eventName, filters) => {
return await this.contract.getPastEvents(eventName, filters);
}
joinContract = async(publicKeyBuffer, callback) => {
var publicKeyLeft = '0x' + publicKeyBuffer.toString('hex', 0, 32);
var publicKeyRight = '0x' + publicKeyBuffer.toString('hex', 32, 64);
this.transactionManager.executeMethod(this.contract.methods.join(publicKeyLeft, publicKeyRight))
.on(Constant.EVENT.ON_APPROVED, (txHash) => {
if (callback) callback(Constant.EVENT.ON_APPROVED);
})
.on(Constant.EVENT.ON_REJECTED, (txHash) => {
if (callback) callback(Constant.EVENT.ON_REJECTED);
})
.on(Constant.EVENT.ON_RECEIPT, (receipt) => {
if (callback) callback(Constant.EVENT.ON_RECEIPT);
})
.on(Constant.EVENT.ON_ERROR, (error, txHash) => {
appDispatcher.dispatch({
action: Constant.EVENT.ENCOUNTERED_ERROR,
message: error.message,
title: "Error"
});
if (callback) callback(Constant.EVENT.ON_ERROR);
});
}
// joinContract = async (publicKeyBuffer, callback) => {
addContact = async (address, callback) => {
console.log(address);
var method = this.contract.methods.addContact(address);
this.transactionManager.executeMethod(method)
.on(Constant.EVENT.ON_APPROVED, (txHash) => {
if (callback) callback(Constant.EVENT.ON_APPROVED);
})
.on(Constant.EVENT.ON_RECEIPT, (receipt) => {
if (callback) callback(Constant.EVENT.ON_RECEIPT);
})
.on(Constant.EVENT.ON_ERROR, (error, txHash) => {
appDispatcher.dispatch({
action: Constant.EVENT.ENCOUNTERED_ERROR,
message: error.message,
title: "Error"
});
if (callback) callback(Constant.EVENT.ON_ERROR);
});
}
acceptContactRequest = async (address, callback) => {
var method = this.contract.methods.acceptContactRequest(address);
this.transactionManager.executeMethod(method)
.on(Constant.EVENT.ON_APPROVED, (txHash) => {
if (callback) callback(Constant.EVENT.ON_APPROVED);
})
.on(Constant.EVENT.ON_RECEIPT, (receipt) => {
if (callback) callback(Constant.EVENT.ON_RECEIPT);
})
.on(Constant.EVENT.ON_ERROR, (error, txHash) => {
appDispatcher.dispatch({
action: Constant.EVENT.ENCOUNTERED_ERROR,
message: error.message,
title: "Error"
});
if (callback) callback(Constant.EVENT.ON_ERROR);
});
}
updateProfile = async (name, avatarUrl, callback) => {
var nameHex = '0x' + Buffer.from(name, 'ascii').toString('hex');
var avatarUrlHex = '0x' + Buffer.from(avatarUrl, 'ascii').toString('hex');
var method = this.contract.methods.updateProfile(nameHex, avatarUrlHex);
this.transactionManager.executeMethod(method)
.on(Constant.EVENT.ON_APPROVED, (txHash) => {
if (callback) callback(Constant.EVENT.ON_APPROVED);
})
.on(Constant.EVENT.ON_RECEIPT, (receipt) => {
if (callback) callback(Constant.EVENT.ON_RECEIPT);
})
.on(Constant.EVENT.ON_ERROR, (error, txHash) => {
appDispatcher.dispatch({
action: Constant.EVENT.ENCOUNTERED_ERROR,
message: error.message,
title: "Error"
});
if (callback) callback(Constant.EVENT.ON_ERROR);
});
}
// A message will be encrypted locally before sending to the smart contract
sendMessage = async (toAddress, publicKey, message) => {
var publicKeyBuffer = Buffer.from(publicKey, 'hex');
var encryptedRaw = utils.encrypt(message, this.accountManager.computeSecret(publicKeyBuffer));
var encryptedMessage = '0x' + encryptedRaw.toString('hex');
var method = this.contract.methods.sendMessage(toAddress, encryptedMessage, utils.getEncryptAlgorithmInHex());
this.transactionManager.executeMethod(method)
.on(Constant.EVENT.ON_APPROVED, (txHash) => {
this.storageManager.addMyLocalMessage(encryptedMessage, toAddress, utils.getEncryptAlgorithm(), txHash);
appDispatcher.dispatch({
action: Constant.EVENT.MESSAGES_UPDATED,
data: toAddress
});
})
.on(Constant.EVENT.ON_REJECTED, (data) => {
// do nothing
})
.on(Constant.EVENT.ON_RECEIPT, (receipt, ) => {
this.storageManager.updateLocalMessage(toAddress, receipt.transactionHash, Constant.SENT_STATUS.SUCCESS);
appDispatcher.dispatch({
action: Constant.EVENT.MESSAGES_UPDATED,
data: toAddress
});
})
.on(Constant.EVENT.ON_ERROR, (error, txHash) => {
this.storageManager.updateLocalMessage(toAddress, txHash, Constant.SENT_STATUS.FAILED);
appDispatcher.dispatch({
action: Constant.EVENT.MESSAGES_UPDATED,
data: toAddress
});
});
}
}
export default ContractManager;
enter image description here
Here, I'm trying to call the function 'joinContract' {the function of ContractManager class} in HeaderMenu.js using the function handleJoinClicked().
And,boom... code is getting crash after clicking the join button. It's showing the error. This[coontractManager.joinContract] is not a function.
Please help.
I have an obstacle, that is if I press the cancel order button (batalkan pesanan) then an infinite loop appears, why do I use componentdidupdate because to update the new display or is there a better method besides componentdidupdate?
before pressing cancel order (batalkan pesanan
infinite loop occurs
the function is to update if after pressing the cancel order (batalkan pesanan) button the image below appears
this code :
import React, { Component } from "react";
import { Tabs, Spin } from "antd";
import { CustomTabPane } from "../../components/CustomTabDashboard";
import OrderListWaitingInDelivery from "../OrderListWaitingInDelivery";
import OrderListWaitingFinish from "../OrderListWaitingFinish";
import OrderListWaitingNotSent from "../OrderListWaitingNotSent";
import OrderListWaitingNotPay from "../OrderListWaitingNotPay";
import OrderDetailsDashboard from "../OrderDetailsDashboard";
import OrderDetailsCancel from "../OrderDetailsCancel";
import OrderListWaitingCancel from "../OrderListWaitingCancel";
import { apiGetWithToken } from "../../api/services";
import { PATH_DASHBOARD_TAB } from "../../api/path";
import NoOrderHistory from "../../components/NoOrderHistory";
const keyFnNames = {
'1': 'updateTabNotPay',
'2': 'updateTabNotSent',
'3': 'updateTabInDelivery',
'4': 'updateTabFinish',
'5': 'updateTabCancel'
};
class CustomerOderNavigation extends Component {
constructor(props) {
super(props);
this.state = {
isShowOrderDetailsDashboard: false,
orderId: null,
activeKey: "1",
loading: false,
productOrderNotYetPay: [],
productOrderNotYetSent: [],
productOrderInDelivery: [],
productOrderFinish: [],
productOrderCancel: []
};
}
componentDidMount(){
this.productOrderTabsNotYetPay();
}
componentDidUpdate() {
this.productOrderTabsNotYetPay();
//this.productOrderTabsNotYetSent();
// this.productOrderTabsInDelivery();
// this.productOrderTabsFinish();
// this.productOrderTabsCancel();
};
componentWillUnmount() {
this.setState({
loading: false
});
}
actionShowOrderListWaiting = () => {
this.setState({
isShowOrderDetailsDashboard: !this.state.isShowOrderDetailsDashboard
});
};
actionShowOrderDetailsDashboard = (orderId) => {
this.actionShowOrderListWaiting();
this.setState({
orderId: orderId
})
};
productOrderTabsNotYetPay = async () => {
try {
const response = await apiGetWithToken(PATH_DASHBOARD_TAB.ORDER_STATUS_NOT_YET_PAID);
const productOrderTabsNotYetPay = {
productOrderNotYetPay: response.data.data
};
this.setState({
...productOrderTabsNotYetPay,
loading: true
});
} catch (error) {
console.log(error);
this.setState({ loading: false });
}
};
productOrderTabsNotYetSent = async () => {
try {
const response = await apiGetWithToken(PATH_DASHBOARD_TAB.ORDER_STATUS_NOT_YET_SENT);
const productOrderTabsNotYetSent = {
productOrderNotYetSent: response.data.data,
loading: true
};
this.setState({
...productOrderTabsNotYetSent
});
} catch (error) {
console.log(error);
this.setState({ loading: false });
}
};
productOrderTabsInDelivery = async () => {
try {
const response = await apiGetWithToken(PATH_DASHBOARD_TAB.ORDER_STATUS_IN_DELIVERY);
const productOrderTabsInDelivery = {
productOrderInDelivery: response.data.data
};
this.setState({
...productOrderTabsInDelivery,
loading: true
});
} catch (error) {
console.log(error);
this.setState({ loading: false });
}
};
productOrderTabsFinish = async () => {
try {
const response = await apiGetWithToken(PATH_DASHBOARD_TAB.ORDER_STATUS_FINISH);
const productOrderTabsFinish = {
productOrderFinish: response.data.data
};
this.setState({
...productOrderTabsFinish,
loading: true
});
} catch (error) {
console.log(error);
this.setState({ loading: false });
}
};
productOrderTabsCancel = async () => {
try {
const response = await apiGetWithToken(PATH_DASHBOARD_TAB.ORDER_STATUS_CANCEL);
const productOrderTabsCancel = {
productOrderCancel: response.data.data
};
this.setState({
...productOrderTabsCancel,
loading: true
});
} catch (error) {
console.log(error);
this.setState({ loading: false });
}
};
updateTabNotPay = () => {
this.productOrderTabsNotYetPay();
};
updateTabNotSent = () => {
this.productOrderTabsNotYetSent();
};
updateTabInDelivery = () => {
this.productOrderTabsInDelivery();
};
updateTabFinish = () => {
this.productOrderTabsFinish();
};
updateTabCancel = () => {
this.productOrderTabsCancel();
};
handleChange = (selectedkey) => {
this.setState({ activeKey: selectedkey })
const fnName = keyFnNames[selectedkey];
if (fnName) {
this[fnName]();
}
};
render() {
return (
<Tabs activeKey={this.state.activeKey} onChange={this.handleChange} >
<CustomTabPane
key={"1"}
tab={
<span
onClick={() =>
this.setState({
isShowOrderDetailsDashboard: false
})}
>{"Belum Bayar"}</span>}
my_prop={
this.state.productOrderNotYetPay.length < 1 ?
(<Spin tip="Loading..." spinning={this.state.loading} delay={500}>
<NoOrderHistory />
</Spin>
) : (
this.state.isShowOrderDetailsDashboard === false ?
(<OrderListWaitingNotPay
productOrderNotYetPay={this.state.productOrderNotYetPay}
actionShowOrderDetailsDashboard={this.actionShowOrderDetailsDashboard}
tabsNotPay={1}
/>) : (
<OrderDetailsDashboard
orderId={this.state.orderId}
actionShowOrderListWaiting={() => this.actionShowOrderListWaiting()}
tabsNotPay={1}
/>)
)
}
/>
<CustomTabPane
key={"2"}
tab={<span
onClick={() =>
this.setState({
isShowOrderDetailsDashboard: false
})}>{"Sedang Diproses"}</span>}
my_prop={
this.state.productOrderNotYetSent.length < 1 ?
(<Spin tip="Loading..." spinning={this.state.loading} delay={500}>
<NoOrderHistory /></Spin>
) : (
this.state.isShowOrderDetailsDashboard === false ?
<OrderListWaitingNotSent
actionShowOrderDetailsDashboard={this.actionShowOrderDetailsDashboard}
productOrderNotYetSent={this.state.productOrderNotYetSent}
tabsNotSent={2}
/> : (
<OrderDetailsDashboard orderId={this.state.orderId}
actionShowOrderListWaiting={() => this.actionShowOrderListWaiting()}
tabsNotSent={2}
/>)
)
}
/>
<CustomTabPane
key={"3"}
tab={<span
onClick={() =>
this.setState({
isShowOrderDetailsDashboard: false
})}>
{"Dalam Pengiriman"}
</span>}
my_prop={
this.state.productOrderInDelivery.length < 1 ?
(<Spin tip="Loading..." spinning={this.state.loading} delay={500}>
<NoOrderHistory /></Spin>
) : (
this.state.isShowOrderDetailsDashboard === false ?
<OrderListWaitingInDelivery
productOrderInDelivery={this.state.productOrderInDelivery}
actionShowOrderDetailsDashboard={this.actionShowOrderDetailsDashboard}
tabsInDelivery={3}
/> : (
<OrderDetailsDashboard orderId={this.state.orderId}
actionShowOrderListWaiting={() => this.actionShowOrderListWaiting()}
tabsInDelivery={3}
/>)
)
} />
<CustomTabPane
key={"4"}
tab={<span
onClick={() =>
this.setState({
isShowOrderDetailsDashboard: false
})}>{"Selesai"}</span>}
my_prop={
this.state.productOrderFinish.length < 1 ?
(<Spin tip="Loading..." spinning={this.state.loading} delay={500}>
<NoOrderHistory /></Spin>
) : (
this.state.isShowOrderDetailsDashboard === false ?
<OrderListWaitingFinish
productOrderFinish={this.state.productOrderFinish}
actionShowOrderDetailsDashboard={this.actionShowOrderDetailsDashboard}
tabsFinish={4}
/> : (
<OrderDetailsDashboard orderId={this.state.orderId}
actionShowOrderListWaiting={() => this.actionShowOrderListWaiting()}
tabsFinish={4}
/>)
)
} />
<CustomTabPane
key={"5"}
tab={<span
onClick={() =>
this.setState({
isShowOrderDetailsDashboard: false
})}>{"Batal"}</span>}
my_prop={
this.state.productOrderCancel.length < 1 ?
(<Spin tip="Loading..." spinning={this.state.loading} delay={500}>
<NoOrderHistory /></Spin>
) : (
this.state.isShowOrderDetailsDashboard === false ?
<OrderListWaitingCancel
productOrderCancel={this.state.productOrderCancel}
actionShowOrderDetailsDashboard={this.actionShowOrderDetailsDashboard}
/> : (
<OrderDetailsCancel orderId={this.state.orderId}
actionShowOrderListWaiting={() => this.actionShowOrderListWaiting()}
/>)
)
}
/>
</Tabs>
);
}
}
export default CustomerOderNavigation;
You're running into an infinite loop because you're calling setState inside of the function you're calling in componentDidUpdate.
Calling setState in componentDidUpdate will always cause a re-render
To mitigate this, you should enclose the function you're calling inside a conditional expression.
componentDidUpdate(prevProps, prevState) {
// yourConditionalExpression can be any of the following:
// this.props.yourPropName !== prevProps.yourPropName
// this.state.yourStateVariable !== prevState.yourStateVariable
if (yourConditionalExpression) {
this.productOrderTabsNotYetPay();
}
}