React updates the component but not the actual HTML weird behavior - javascript

I am experiencing a very weird react behavior. There is this component which gets message and emailSubmitedText. In render method based on some condition it should render either first or the other one.
Now at first it is message. I click on the submit of the form. All the functions happen.
The component rerenders. In the console log I can see this time it should render emailSubmitedText. In react devtools it show the right text.
However in the actual html and html inspector it still shows the previos text.
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import Details from './Details'
class DefaultMessage extends Component {
inputRef = null
renderEmailForm = () => {
return (
<form
className='b2c_email-form input-field'
onSubmit={e => {
e.preventDefault()
const { projectId, visitSessionId } = this.props
this.setState({ email: this.inputRef.value })
this.props.onSubmitEmail({
email: this.inputRef.value,
convertedPage: window.location.href, projectId, visitSessionId
})
}}
>
<div className="input-field">
<input ref={elem => this.inputRef = elem} id='email' type='email' className='validate' value={this.props.email} />
<label htmlFor='email' data-error='Invalid email address'>E-mail</label>
</div>
<button
className='b2c_email-form-button waves-effect waves-light btn'
type='submit'
style={{
backgroundColor: this.props.companyColor || '#63bc78'
}}
>Submit</button>
</form>
)
}
render = () => {
console.log('...> ', this.props.error || !this.props.contactId && this.props.message || this.props.emailSubmitedText)
return (
<div className='b2c_chat-message'>
<Details
classNames='b2c_chat-message-details__admin'
avatar={this.props.avatar}
name={this.props.name}
date={this.props.date}
/>
<div className='b2c_chat-message-text b2c_chat-message-text__admin b2c_chat-message-default'>
<div className='b2c_chat-message-after b2c_chat-message-after__admin' />
{this.props.error || !this.props.contactId && this.props.message || this.props.emailSubmitedText}
{!this.props.contactId && this.renderEmailForm()}
</div>
</div>
)
}
}
DefaultMessage.propTypes = {
projectId: PropTypes.string.isRequired,
visitSessionId: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
email: PropTypes.string.isRequired,
date: PropTypes.string.isRequired,
message: PropTypes.string.isRequired,
onSubmitEmail: PropTypes.func.isRequired
}
export default DefaultMessage
Here is the direct parent of the component.
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import $ from 'jquery'
import moment from 'moment'
import randomstring from 'randomstring'
import DefaultMessage from './DefaultMessage'
import Message from './Message'
import UserTypingIndicator from '../UserTypingIndicator'
import TypingIndicator from './TypingIndicator'
class Messages extends Component {
chatRef = null
componentDidUpdate () {
this.scrollToTheLastMessage()
}
scrollToTheLastMessage = () => {
const $chat = $(this.chatRef)
const scrollTop = $chat.prop('scrollHeight') - $chat.innerHeight()
$chat.scrollTop(scrollTop)
}
renderDefaultMessage = () => (
<DefaultMessage
contactId={this.props.contactId}
companyColor={this.props.companyColor}
error={this.props.error}
date={moment().format('h:mm A')}
name={this.props.adminName}
avatar={this.props.adminAvatar}
message={this.props.welcomeMessage}
emailSubmitedText={this.props.emailSubmitedText}
projectId={this.props.projectId}
visitSessionId={this.props.visitSessionId}
onSubmitEmail={this.props.onSubmitEmail}
/>
)
renderMessages = () => {
let checkConversationDate = null
const {messages, contactName, adminName, adminAvatar} = this.props
const compareConversationDates = (createdAt) => {
checkConversationDate = moment(createdAt).format("DD.MM.YYYY")
return (
<div key={randomstring.generate()} className='conversationDayDate'>
<span>{checkConversationDate}</span>
</div>
)
}
if (!messages) return null
return messages.map((message, index) => {
return (
<div>
{checkConversationDate !== moment(message.createdAt.$date).format("DD.MM.YYYY") ? compareConversationDates(message.createdAt.$date) : ''}
{/* {index === 0 ? this.renderDefaultMessage() : ''} */}
<Message
isAdmin={message.userId ? true : false}
imageFile={message.imageFile}
key={randomstring.generate()}
companyColor={this.props.companyColor}
contactName={contactName}
adminName={adminName}
avatar={adminAvatar}
message={message.message}
date={moment(message.createdAt.$date).format('h:mm A')}
/>
</div>
)
})
}
renderTypingIndicators = () => {
const arrayToRender = [
this.props.isAdminTyping && <AdminTypingIndicator />,
this.props.isUserTyping && <UserTypingIndicator />
]
return arrayToRender
}
render = () => <div ref={elem => this.chatRef = elem} id='chat' className='chat-body' style={{
height: 'calc(100% - 190px - 3rem)',
overflowY: 'scroll',
margin: '30px 10px 10px 0',
boxSizing: 'border-box'
}}>
{this.renderDefaultMessage()}
{this.renderMessages()}
{this.renderTypingIndicators()}
</div>
}
Messages.propTypes = {
projectId: PropTypes.string.isRequired,
visitSessionId: PropTypes.string.isRequired,
messages: PropTypes.array.isRequired,
adminName: PropTypes.string.isRequired,
contactName: PropTypes.string.isRequired,
onSubmitEmail: PropTypes.func.isRequired
}
export default Messages
And here is where Container with states
import React, { Component } from 'react'
import Sound from 'react-sound'
import ddp from '../../ddp'
import Cookies from 'js-cookie'
import randomstring from 'randomstring'
import ChatContainer from './ChatContainer'
import Icon from './Icon'
import { connect, makeArrayCollectionFromObjectCollection, getNewMessages } from '../../functions'
class View extends Component {
defaultDocumentTitle = null
state = {
contactId: '',
chat: null,
show: false,
newMessagesCount: null,
notStatus: 'STOPPED'
}
newMessageNotification = newMessages => {
if (newMessages.length && newMessages.length > this.state.newMessagesCount) {
this.setState({ notStatus: 'PLAYING' })
document.title = `(${newMessages.length}) ${this.defaultDocumentTitle}`
} else if (!newMessages.length) {
document.title = this.defaultDocumentTitle
}
if (this.state.newMessagesCount !== newMessages.length) {
this.setState({ newMessagesCount: newMessages.length })
}
}
componentWillMount () {
this.defaultDocumentTitle = document.title
}
componentDidMount = async () => {
this.setContactIdFromCookies()
await connect(ddp)
}
setContactIdFromCookies = () => {
window.Cookies = Cookies
console.warn('setContactIdFromCookies')
const contactId = Cookies.get('b2cContactId')
console.log('contactId', contactId)
if (contactId) this.setState({contactId})
}
componentDidUpdate () {
console.warn('componentDidUpdate', this.props)
if (this.state.contactId && !this.state.chat) {
this.getChat(this.state.contactId)
}
if (this.state.chat && this.state.chat.length) {
let newMessages = getNewMessages(this.state.chat)
this.newMessageNotification(newMessages)
}
}
componentWillReceiveProps = (nextProps) => {
console.warn('componentWillReceiveProps', nextProps)
if (!nextProps.contactId) return
if (this.state.chat == null) this.getChat(nextProps.contactId)
}
getChat = async (contactId) => {
console.log('getChat', contactId)
await ddp.subscribe('Messages', {contactId})
const messagesColl = ddp.getCollection('Messages')
console.log('messagesColl', messagesColl)
this.setState({chat: this.getMessages(messagesColl)})
ddp.watch('Messages', (changedDoc, message) => {
console.log('Messages collection item changed', changedDoc, message)
const messagesColl = ddp.getCollection('Messages')
this.setState({chat: this.getMessages(messagesColl)})
})
}
getMessages = collection => {
let messages = []
if (collection) {
messages = makeArrayCollectionFromObjectCollection(collection)
}
console.log('messages', messages)
return messages
}
submitEmail = ({ email, convertedPage, projectId, visitSessionId }) => ddp.call('chat.init', { email, convertedPage, projectId, visitSessionId })
.then(contactId => {
Cookies.set('b2cContactId', contactId, { expires: 90 })
this.setState({ contactId, error: '' })
})
.catch(error => {
console.error('Error >', error)
})
readMessages = () => ddp.call('readMessages', {contactId: this.state.contactId, userId: !null})
.then(res => {
console.log('res', res)
})
.catch(error => {
console.error('Error', error)
})
submitMessage = ({message, visitSessionId, imageFile}) => ddp.call('chat.submitContactMessage', { message, visitSessionId, contactId: this.state.contactId, projectId: this.props.projectId, imageFile: imageFile || null})
.then((res) => {
console.log('res', res)
})
.catch(error => {
console.error('Error', error)
this.setState({error})
})
toggleChat = () => this.setState((state) => ({show: !state.show}))
sendFileToServer = (base64File, resolve, reject) => {
ddp.call('uploadToDropbox', base64File)
.then((res) => {
this.submitMessage({message: '', visitSessionId: this.props.visitSessionId, imageFile: res})
console.log('res', res)
})
.catch(error => {
console.error('Error', error)
})
}
getBase64 = (file, resolve, reject) => {
const self = this
const reader = new FileReader()
reader.readAsDataURL(file)
reader.onload = function () {
self.sendFileToServer(reader.result, resolve, reject)
}
reader.onerror = function (error) {
console.error('FileReader Error: ', error)
}
}
onFileDrop = files => {
let self = this
files.forEach(file => {
return new Promise((resolve, reject) => {
self.getBase64(file, resolve, reject)
})
})
}
render () {
return (
<div>
<ChatContainer
onFileDrop={this.onFileDrop}
contactId={this.state.contactId}
show={this.state.show}
error={this.state.error && <span style={{color: 'red'}}>{this.state.error}</span>}
chatSettings={this.props.chatSettings}
projectId={this.props.projectId}
visitSessionId={this.props.visitSessionId}
defaultAdminUserName='default defaultAdminUserName'
contactName='You'
supportName='Our Support'
messages={this.state.chat}
onSend={this.submitMessage}
onSubmitEmail={this.submitEmail}
toggleChat={this.toggleChat}
readMessages={this.readMessages}
/>
<Icon
companyColor={this.props.chatSettings.companyColor}
onClick={this.toggleChat}
newMessagesCount={this.state.newMessagesCount}
/>
<Sound
url='https://www.incredo.co/hubfs/b2c/Not%201.wav'
playStatus={this.state.notStatus}
playFromPosition={0}
onFinishedPlaying={() => this.setState({ notStatus: 'STOPPED' })}
/>
</div>
)
}
}
export default View

Related

How to get a property in an array

I need to make an update method in my application. But I'm not able to filter the specific id of the task to put as a parameter of the request. If I use filter/map it returns the two indices of the array. But through my click, I want to filter the id that I clicked. How can I do this?
import Modal from 'react-modal'
import { useMutation } from 'react-query'
import ReactQuill from 'react-quill'
import { toast } from 'react-toastify'
import { useForm } from '../../hooks/useForm'
import { useTasks } from '../../hooks/useTasks'
import { api } from '../../services/api'
import { queryClient } from '../../services/queryClient'
import { modules } from '../../utils/modules'
import { ButtonContainer, CancelButton, SaveButton } from './styles'
type UpdateNoteModalProps = {
isOpen: boolean
onRequestClose: () => void
}
type UpdateNoteData = {
id: string
form?: { [key: string]: string | number }
}
export const UpdateNoteModal = ({
isOpen,
onRequestClose
}: UpdateNoteModalProps) => {
const { data } = useTasks()
const { form, handleInputChange } = useForm({
initialState: {
description: ''
}
})
const id = data?.filter((note: any) => note._id)
// .filter((note: any) => note.id === note.id)
// const id = findId.find((note: any) => note.id === note.id)
console.log({ id })
const updateNote = useMutation(
async ({ id, form }: UpdateNoteData) => {
const response = await api.put(`/task/${id}`, form)
const token = localStorage.getItem('token')
if (token) {
api.defaults.headers.common.Authorization = token
}
return response.data
},
{
onSuccess: () => {
queryClient.invalidateQueries('task')
onRequestClose()
toast.success('🦄 Sua nota foi atualizada com sucesso!', {
position: 'top-center',
autoClose: 5000
})
},
onError: () => {
toast.error('🦄 Ocorreu um erro, tente novamente mais tarde!', {
position: 'top-center',
autoClose: 5000
})
}
}
)
const handleSubmit = async () => {
// event.preventDefault()
await updateNote.mutateAsync({ id, form })
}
return (
<Modal
isOpen={isOpen}
onRequestClose={onRequestClose}
className="react-modal-content"
overlayClassName="react-modal-overlay"
>
<h2>Editar</h2>
<ReactQuill
modules={modules}
theme="snow"
className="toolbar"
onChange={handleInputChange}
/>
{/* <input
type="text"
name="description"
value={form.description}
onChange={handleInputChange}
/> */}
<ButtonContainer>
<div onClick={onRequestClose}>
<CancelButton>Cancelar</CancelButton>
</div>
<div>
<SaveButton onClick={() => handleSubmit()}>Salvar</SaveButton>
</div>
</ButtonContainer>
</Modal>
)
}
Maybe you can do something like:
const index = data.map(x => x.id).indexOf(x === id)
And then you can use that index to get the data piece you want e.g. const foundItem = data[index]

How to render an asynchronous result (array) in a component in react?

I have been doing js for about a month now, and I am writing this program where I am using clarifai API to see which celebrity a person on the photo resembles the most.
I want to pass the output as props to Rank component to render it, but
I get the
Type error: clarifaiResults.map is not a function at App.transformResponse
Basically, the response I want to pass as props is the
const clarifaiResults = response.outputs[0].data.regions[0].data.concepts[0].name;
part that I get in console.log now
I am assuming it's because there is no output yet when the app tries to render the component, but I can't figure out what's wrong with the code. Thank you!
App.js
import React, { Component } from 'react';
import './App.css';
import SignIn from './Component/SignIn/SignIn.js';
import Register from './Component/Register/Register.js';
import Particles from 'react-particles-js';
import Logo from './Component/Logo/Logo.js';
import Navigation from './Component/Navi/Navigation.js';
import ImageLinkForm from './Component/Form/ImageLinkForm.js';
import Rank from './Component/Rank/Rank.js'
import Clarifai from 'clarifai';
import FaceRecognition from './Component/Face/FaceRecognition.js';
import FaceComparison from './Component/Comparison/FaceComparison.js';
const app = new Clarifai.App({
apiKey: 'MYSUPERSECRETKEY'
});
const initialState = {
input: "",
imageUrl: "",
results: [],
route: "SignIn",
user: {
id: "",
name: "",
email: "",
entries: 0,
joined: "",
},
};
const particleOptions = {
particles: {
number: {
value: 40,
density: {
enable: true,
value_area: 800,
},
}
}
}
class App extends Component{
constructor() {
super();
this.state = initialState;
}
transformResponse = (response) => {
const clarifaiResults = response.outputs[0].data.regions[0].data.concepts[0].name;
const results = clarifaiResults.map((ingredient) => ({
ingredients: ingredient.name,
probablitiy: ingredient.value,
}));
this.setState({results: results.celebrityName});
return {results: []};
};
onInputChange = (event) => {
this.setState({input: event.target.value});
}
onSubmit = () => {
this.setState({imageUrl: this.state.input});
app.models
.predict(
Clarifai.CELEBRITY_MODEL,
this.state.input)
.then(response => {
console.log(response.outputs[0].data.regions[0].data.concepts[0].name)
if (response) {
fetch ('http://loclhost:3000', {
method: 'post',
headers: {'Conent-Type' : 'application/json'},
body: JSON.stringify({
input: this.state.user.input
})
})
.then((response) => response.json())
.then(count => {
this.setState(Object.assign(this.state.user, {entries:count}))
})
}
this.transformResponse(response);
})
.catch(err => console.log(err));
};
;
onRouteChange = (route) => {
if (route === 'signout'){
this.setState({isSignedIn: false})
} else if (route ==='home'){
this.setState({isSignedIn: true})
}
this.setState({route: route});
}
render() {
let { isSignedIn, imageUrl, route, results} = this.state;
return (
<div className="App">
<Particles className='particles'
params={particleOptions}
/>
<Navigation isSignedIn={isSignedIn} onRouteChange={this.onRouteChange}/>
{ route ==='home'
? <div>
<Logo />
<Rank
results = {results}/>
<ImageLinkForm
onInputChange={this.onInputChange}
onSubmit={this.onSubmit}
/>
<FaceRecognition
imageUrl={imageUrl}
/>
<FaceComparison
results = {results}
/>
</div>
: (
route === 'SignIn'
? <SignIn onRouteChange={this.onRouteChange}/>
: <Register />
)
}
</div>
);
};
}
export default App;
Rank.js
import React from 'react';
const Rank = ({results}) => {
const prediction = results.map((result) => {
const {ingredients} = result;
return (
<div>
<li className="celebrityName">{ingredients}</li>
</div>
);
});
if (prediction && prediction.length>1) {
return (
<div>
<div className='white f3'>
You look a lot like...
</div>
<div className='white f1'>
{results}
</div>
</div>
);
} else {
return (
<div>
</div>
)
}
};
export default Rank;

setState not resetting state / switching between modals

I asked similar question earlier, but didn't get much back. I have two modals for user auth: join and login. Each modal has a link to the other one. Displayed login errors persist when you click on the "sign up" and switch to the join modal and vise versa. I tried to set the state.errors to empty array, but the errors still persist. I changed handleSwitch to callback. The errors array still has length. I tried using switched as part of the state, resetting it to true in handleSwitch and ternary, no result either. Can anybody suggest an alternative solution.
import React from 'react';
class Login extends React.Component {
constructor(props) {
super(props);
this.state = {
email: '',
password: '',
errors: [],
switched: false
};
this.handleSubmit = this.handleSubmit.bind(this);
this.handleSwitch = this.handleSwitch.bind(this);
this.mapErrors = this.mapErrors.bind(this);
this.handleErrors = this.handleErrors.bind(this);
}
componentDidMount() {
this.setState({ errors: this.props.errors})
}
componentDidUpdate(prev) {
if (prev.errors.length !== this.props.errors.length) {
this.setState( {errors: this.props.errors} )
}
}
handleInput(type) {
return (err) => {
this.setState({ [type]: err.currentTarget.value })
};
}
handleSubmit(event) {
event.preventDefault();
const user = Object.assign({}, this.state);
this.props.processForm(user)
// .then(() => this.props.history.push('/users')); //change to /videos later
}
handleSwitch() {
// debugger
this.setState({ errors: [] }, function () {
this.props.openModal('signup')
});
// debugger
}
mapErrors() {
if (this.state.errors.length) {
return this.state.errors.map((error, i) => {
return <p key={i}>{error}</p>
})
}
}
handleErrors() {
debugger
if (!this.state.switched) {
return <div className="errors">{this.mapErrors}</div>
} else {
return null;
}
};
render() {
console.log(this.state.errors)
return (
<div className="login-form">
<div>
<h2 className="login-header">Log in to Foxeo</h2>
</div>
<form>
<input className="login-email"
type="text"
value={this.state.email}
placeholder="Email address"
onChange={this.handleInput('email')}
/>
<input className="login-password"
type="password"
value={this.state.password}
placeholder="Password"
onChange={this.handleInput('password')}
/>
<div className="errors">{this.mapErrors()}</div>
{/* { this.state.switched ?
<div className="errors">{this.handleErrors()}</div> :
<div className="errors">{this.mapErrors()}</div>
} */}
<button className="login-button" onClick={this.handleSubmit}>Log in with email</button>
<div className="login-footer">Don't have an account?
{/* <button className="login-form-btn" onClick={() => this.props.openModal('signup')}>Join</button> */}
<button className="login-form-btn" onClick={ this.handleSwitch}> Join</button>
</div>
</form>
</div>
);
}
};
export default Login;
I suggest getting the new errors from the props instead of from state:
mapErrors() {
if (this.props.errors.length) {
return this.props.errors.map((error, i) => {
return <p key={i}>{error}</p>
})
Dispatching resetErrors action solved the issue. The handleSwitch method is quite simple:
handleSwitch() {
this.props.resetErrors()
this.props.openModal('signup')
}
session actions:
import * as apiUtil from '../util/session_api_util';
export const RECEIVE_CURRENT_USER = 'RECEIVE_CURRENT_USER';
export const LOGOUT_CURRENT_USER = 'LOGOUT_CURRENT_USER';
export const RECEIVE_ERRORS = 'RECEIVE_ERRORS';
export const CLEAR_ERRORS = 'CLEAR_ERRORS';
const receiveErrors = (errors) => ({
type: RECEIVE_ERRORS,
errors
})
const clearErrors = () => ({
type: CLEAR_ERRORS,
errors: []
})
const receiveCurrentUser = (user) => ({
type: RECEIVE_CURRENT_USER,
user
});
const logoutCurrentUser = () => ({
type: LOGOUT_CURRENT_USER
});
export const signup = user => dispatch => (
apiUtil.signup(user).then(user => (
dispatch(receiveCurrentUser(user))
), err => (
dispatch(receiveErrors(err.responseJSON))
))
);
export const login = user => dispatch => {
return apiUtil.login(user).then(user => {
dispatch(receiveCurrentUser(user))
}, err => (
dispatch(receiveErrors(err.responseJSON))
))
};
export const logout = () => dispatch => apiUtil.logout()
.then(() => dispatch(logoutCurrentUser()));
export const resetErrors = () => dispatch(clearErrors());
session errors reducer:
import { RECEIVE_ERRORS, RECEIVE_CURRENT_USER, CLEAR_ERRORS } from '../actions/session_actions';
const sessionErrorsReducer = (state = [], action) => {
Object.freeze(state);
switch (action.type) {
case RECEIVE_ERRORS:
return action.errors;
case CLEAR_ERRORS:
return [];
case RECEIVE_CURRENT_USER:
return [];
default:
return state;
}
};
export default sessionErrorsReducer;

Getting error while calling the function of ContractManager in HeaderMenu

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.

Delete request with axios - React

I'm trying to create small app based on Json server package which will help me to remember movies I want to watch when I have free time, want to learn React and Axios so I'm doing it with these technologies , Idea is when I click on add movie button - movie will be added to Json database,
when click on delete - particular movie will be deleted
and when click on the list - I will be able to edit text,
Delete works if I do something like http://localhost:3000/movies/1, to show what id should it delete, but is there any way to set it? To delete the list connected to button I'm clicking at? something like http://localhost:3000/movies/"id"? I will be grateful for any help as I totally don't have any idea how to move on with it
import React from 'react';
import ReactDom from 'react-dom';
import axios from 'axios';
import List from "./list.jsx";
class Form extends React.Component {
constructor(props) {
super(props)
this.state = {
name:'',
type:'',
description:'',
id:'',
movies: [],
}
}
handleChangeOne = e => {
this.setState({
name:e.target.value
})
}
handleChangeTwo = e => {
this.setState({
type:e.target.value
})
}
handleChangeThree = e => {
this.setState({
description:e.target.value
})
}
handleSubmit = e => {
e.preventDefault()
const url = `http://localhost:3000/movies/`;
axios.post(url, {
name: this.state.name,
type: this.state.type,
description:this.state.description,
id:this.state.id
})
.then(res => {
// console.log(res);
// console.log(res.data);
this.setState({
movies:[this.state.name,this.state.type,this.state.description, this.state.id]
})
})
}
handleRemove = (e) => {
const id = this.state.id;
const url = `http://localhost:3000/movies/`;
// const id = document.querySelectorAll("li").props['data-id'];
e.preventDefault();
axios.delete(url + id)
.then(res => {
console.log(res.data);
})
.catch((err) => {
console.log(err);
})
}
// editMovie = e => {
// const url = `http://localhost:3000/movies/`;
// e.preventDefault();
// const id = e.target.data("id");
// axios.put(url + id, {
// name: this.state.name,
// type: this.state.type,
// description:this.state.description,
// })
// .then(res => {
// console.log(res.data);
// })
// .catch((err) => {
// console.log(err);
// })
// }
render() {
return (
<form onSubmit={this.handleSubmit}>
<input type="text" placeholder="movie" onChange={this.handleChangeOne}/>
<input type="text" placeholder="type of movie" onChange={this.handleChangeTwo}/>
<textarea cols={40} rows={5} placeholder="description of the movie" onChange={this.handleChangeThree}></textarea>
<input type="submit" value="Add movie"></input>
<List removeClick={this.handleRemove} editClick={this.editMovie}/>
</form>
)
}
}
export default Form
List:
import React from 'react';
import ReactDom from 'react-dom';
import axios from 'axios';
class List extends React.Component{
constructor(props){
super(props)
this.state = {
movies: [],
}
}
componentDidMount() {
const url = `http://localhost:3000/movies`;
console.log(url);
axios.get(url)
.then(res => {
console.log(res.data);
const movies = res.data;
this.setState({
movies: movies
})
})
.catch((err) => {
console.log(err);
})
}
// editMovie =(e) => {
// console.log("it works with edit!");
// if (typeof this.props.editClick === "function") {
// this.props.editClick(e)
// } else {
// console.log("Doesn't work with edit");
// }
// }
removeMovie =(e) => {
console.log("it works with remove!");
if (typeof this.props.removeClick === "function") {
this.props.removeClick(e)
} else {
console.log("Doesn't work with remove");
}
}
render(){
let movies = this.state.movies.map(e =>
<ul onClick={this.editMovie}>
<li data-id={e.id}>
{e.name}
</li>
<li data-id={e.id}>
{e.type}
</li>
<li data-id={e.id}>
{e.description}
</li>
<button type="submit" onClick={this.removeMovie}>Delete</button>
</ul>)
return(
<div>
{movies}
</div>
)
}
}
export default List;
Json part
{
"movies": [
{
"id": 1,
"name": "Kongi",
"type": "drama",
"description": "movie about monkey"
},
{
"id": 2,
"name": "Silent Hill",
"type": "thriller",
"description": "movie about monsters"
},
{
"name": "Harry potter",
"type": "fantasy",
"description": "movie about magic and glory",
"id": 3
}
]
}
You could pass the movie object to the removeMovie function in your List component and pass that to the this.props.removeClick function. You could then take the id of the movie to use for your request, and remove the movie from state if the DELETE request is successful.
Example
class Form extends React.Component {
handleRemove = movie => {
const url = `http://localhost:3000/movies/${movie.id}`;
axios
.delete(url)
.then(res => {
this.setState(previousState => {
return {
movies: previousState.movies.filter(m => m.id !== movie.id)
};
});
})
.catch(err => {
console.log(err);
});
};
// ...
}
class List extends React.Component {
removeMovie = (e, movie) => {
e.preventDefault();
if (this.props.removeClick) {
this.props.removeClick(movie);
}
};
// ...
render() {
return (
<div>
{this.state.movies.map(movie => (
<ul onClick={this.editMovie}>
<li data-id={movie.id}>{movie.name}</li>
<li data-id={movie.id}>{movie.type}</li>
<li data-id={movie.id}>{movie.description}</li>
<button type="submit" onClick={e => this.removeMovie(e, movie)}>
Delete
</button>
</ul>
))}
</div>
);
}
}
An simple example using hooks:
const URL = 'https://jsonplaceholder.typicode.com/users'
const Table = () => {
const [employees, setEmployees] = React.useState([])
React.useEffect(() => {
getData()
}, [])
const getData = async () => {
const response = await axios.get(URL)
setEmployees(response.data)
}
const removeData = (id) => {
axios.delete(`${URL}/${id}`).then(res => {
const del = employees.filter(employee => id !== employee.id)
setEmployees(del)
})
}
const renderHeader = () => {
let headerElement = ['id', 'name', 'email', 'phone', 'operation']
return headerElement.map((key, index) => {
return <th key={index}>{key.toUpperCase()}</th>
})
}
const renderBody = () => {
return employees && employees.map(({ id, name, email, phone }) => {
return (
<tr key={id}>
<td>{id}</td>
<td>{name}</td>
<td>{email}</td>
<td>{phone}</td>
<td className='opration'>
<button className='button' onClick={() => removeData(id)}>Delete</button>
</td>
</tr>
)
})
}
return (
<>
<h1 id='title'>React Table</h1>
<table id='employee'>
<thead>
<tr>{renderHeader()}</tr>
</thead>
<tbody>
{renderBody()}
</tbody>
</table>
</>
)
}
ReactDOM.render(<Table />, document.getElementById('root'));

Categories