I am getting error as "Must use destructuring state assignment" in react
I have routed this code in "Home.jsx"
My code is "MainPage.jsx". In this code I am sending userdetails to server and from server I am fetching hashcode value along with userdetails.
MainPage.jsx:
import React, { Component } from 'react';
export class MainPage extends Component {
constructor(props) {
super(props);
this.state = {
userCredientials: {
type: 'credentials',
name: 'vinay',
email: 'vinay3245#gmail.com',
},
mess: '',
userHashCode: '',
};
}
componentDidMount() {
this.connect();
}
/**
* #function connect
* This function establishes the connect with the websocket and also ensures constant
reconnection if connection closes
*/
connect = () => {
const ws = new WebSocket('ws://127.0.0.1:5678/');
// websocket onopen event listener
ws.onopen = () => {
console.log('connected websocket main component');
console.log(this.state);
ws.send(JSON.stringify(this.state.userCredientials)); //line no 32 -- error -- todo
};
// websocket onclose event listener
ws.onclose = () => {
console.log('closed');
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
this.setState({ userHashCode: data.hashCodeOfUser });
if (this.state.userHashCode) { //line no 43 -- error -- todo
this.setState({
mess: `Name: ${data.name} date: ${data.date} HashCode: ${data.hashCodeOfUser} Message: ${data.msg}`,
});
} else {
this.setState({
mess: `Name: ${data.name} Message: ${data.msg}`,
});
}
};
// websocket onerror event listener
ws.onerror = (err) => {
console.error('Socket encountered error: ', err.message, 'Closing socket');
ws.close();
};
};
/**
* utilited by the #function connect to check if the connection is close, if so attempts to
reconnect
*/
check = () => {
const { ws } = this.state;
if (!ws || ws.readyState === WebSocket.CLOSED) this.connect(); // check if websocket
instance is closed, if so call `connect` function.
};
render() {
return (
<div>
<p>{this.state.mess}</p> //line no 72 -- error -- todo
</div>
);
}
}
export default MainPage;
to see error in browser click this link
I have mentioned error in code as "todo"
If any one know please let me know the correct code.
The error means that you can not read this.state.attribute, and must read const { attribute } = this.state instead.
For example, on line 32:
const { userCredientials } = this.state;
ws.send(JSON.stringify(userCredientials));
The error is not a Javascript error per se, but an opinionated one. Javascript can and will compile, but somewhere along your build line that error is triggered. The most common place to write rules like that is in an eslint configuration file.
Related
I am trying to create a web-worker logic into a react custom hook, but unfortunately i noticed
that memory usage is gradual increasing. After a research, i found out that in order to transfer large data between web-workers and main thread,a good practice is to use transferable objects. I tried to add transferable objects, but every time i get following errors:
// postMessage(arrayBuffer , '/', [arrayBuffer]) error:
Uncaught TypeError: Failed to execute 'postMessage' on 'DedicatedWorkerGlobalScope': Overload resolution failed.
// postMessage(arrayBuffer, [arrayBuffer]) error:
Uncaught DOMException: Failed to execute 'postMessage' on 'DedicatedWorkerGlobalScope': Value at index 0 does not have a transferable type.
Any ideas how I can solve that problem (any alternative solutions or any possible web worker improvements) and where the problem is?
.
web-worker main job:
connect to a mqtt client
subscribe to topics
listen to changes for every topic, store all values into a object and every 1 second
send stored topics data object to main thread (notice that data is large)
custom hook main job:
create a web-worker,
in every onmessage event, update redux store
// react custom hook code
import React, { useEffect, useRef } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { setMqttData } from 'store-actions';
const useMqttService = () => {
const dispatch = useDispatch();
const topics = useSelector(state => state.topics);
const workerRef = useRef<Worker>();
useEffect(() => {
workerRef.current = new Worker(new URL('../mqttWorker.worker.js', import.meta.url));
workerRef.current.postMessage({ type: 'CONNECT', host: 'ws://path ...' });
workerRef.current.onmessage = (event: MessageEvent): void => {
dispatch(setMqttData(JSON.parse(event.data)));
// dispatch(setMqttData(bufferToObj(event.data)));
};
return () => {
if (workerRef.current) workerRef.current.terminate();
};
}, [dispatch]);
useEffect(() => {
if (workerRef.current) {
workerRef.current.postMessage({ type: 'TOPICS_CHANGED', topics });
}
}, [topics ]);
return null;
};
// web-worker, mqttWorker.worker.js file code
import mqtt from 'mqtt';
export default class WorkerState {
constructor() {
this.client = null;
this.topics = [];
this.data = {};
this.shareDataTimeoutId = null;
}
tryConnect(host) {
if (host && !this.client) {
this.client = mqtt.connect(host, {});
}
this.client?.on('connect', () => {
this.data.mqttStatus = 'connected';
trySubscribe();
});
this.client?.on('message', (topic, message) => {
const value = JSON.parse(message.toString());
this.data = { ...this.data, [topic]: value };
});
}
trySubscribe() {
if (this.topics.length > 0) {
this.client?.subscribe(this.topics, { qos: 0 }, err => {
if (!err) {
this.tryShareData();
}
});
}
}
tryShareData() {
clearTimeout(this.shareDataTimeoutId);
if (this.client && this.topics.length > 0) {
postMessage(JSON.stringify(this.data));
// Attemp 1, error:
// Uncaught TypeError: Failed to execute 'postMessage' on
// 'DedicatedWorkerGlobalScope': Overload resolution failed.
// const arrayBuffer = objToBuffer(this.data);
// postMessage(arrayBuffer , '/', [arrayBuffer]);
// Attemp 2, error:
// Uncaught DOMException: Failed to execute 'postMessage' on
// 'DedicatedWorkerGlobalScope': Value at index 0 does not have a transferable type.
// const arrayBuffer = objToBuffer(this.data);
// postMessage(arrayBuffer, [arrayBuffer]);
this.shareDataTimeoutId = setTimeout(() => {
this.tryShareData();
}, 1000);
}
}
onmessage = (data) => {
const { type, host = '', topics = [] } = data;
if (type === 'CONNECT_MQTT') {
this.tryConnect(host);
} else if (type === 'TOPICS_CHANGED') {
this.topics = topics;
this.trySubscribe();
}
};
}
const workerState = new WorkerState();
self.onmessage = (event) => {
workerState.onmessage(event.data);
};
// tranform functions
function objToBuffer(obj) {
const jsonString = JSON.stringify(obj);
return Buffer.from(jsonString);
}
function bufferToObj(buffer) {
const jsonString = Buffer.from(buffer).toString();
return JSON.parse(jsonString);
}
i update tranform functions
function objToBuffer(obj){
// const jsonString = JSON.stringify(obj);
// return Buffer.from(jsonString);
const jsonString = JSON.stringify(obj);
const uint8_array = new TextEncoder().encode(jsonString);
const array_buffer = uint8_array.buffer;
return array_buffer;
}
function bufferToObj(array_buffer) {
// const jsonString = Buffer.from(array_buffer).toString();
// return JSON.parse(jsonString);
const decoder = new TextDecoder('utf-8');
const view = new DataView(array_buffer, 0, array_buffer.byteLength);
const string = decoder.decode(view);
const object = JSON.parse(string);
return object;
}
in web-worker file add
const arrayBuffer = objToBuffer(this.data);
postMessage(arrayBuffer, [arrayBuffer]);
finally in custom hook add in onmessage
dispatch(setMqttData(bufferToObj(event.data)));
i am trying to integarte pubnub(replacing socket.io withh pubnub).here below i have shown code.it is working fine for pc.but in mobile devices its not working .i am not getting error as well.any body can tel what i done wrong.
till now i have tried to replace socket with my pubnub connection
import PubNub from 'pubnub';
import { PubNubProvider, usePubNub } from 'pubnub-react';
const pubnub = new PubNub({
publishKey: 'xxxxxxxxxxxx',
subscribeKey: 'xxxxxxxxxxx'
});
//leave Table when close window
const closingCode = () => {
sendMsg("leaveTable");
return null;
};
class App extends Component {
constructor(props) {
super(props);
this.state = {
isOpen: false
};
this.receiveMsg = []
}
window.onbeforeunload = closingCode;
// Read res from service via Socket IO
// socket.on("message", receiveMsg);
socket.on("message", text => {
let params = text.split("|"); //.map(p => Base64.Decode(p)); // we are not using b64 now
let message = params.shift(); // message, eg. playerSitOut, clearTable
this.receiveMsg.push(message);
this.props.updateMessage({ message, params });
});
pubnub.addListener({ message: function (m) {
const channelName = m.channel; // Channel on which the message was published
const channelGroup = m.subscription; // Channel group or wildcard subscription match (if exists)
const pubTT = m.timetoken; // Publish timetoken
const msg = m.message; // Message payload
const publisher = m.publisher; // Message publisher/ } });
//pubnub.subscribe();
}
I am trying to test the function searchTrigger in my CardMain component.
export default class CardMain extends Component {
state = {
Pools : [],
loading: false,
}
componentDidMount(){
axios.get('/pools')
.then (res => {
//console.log(res.data.data);
this.setState({
Pools: res.data.data,
loading: true,
message: "Loading..."
},()=>{
if (res && isMounted){
this.setState({
loading: false
});
}
})
}
)
.catch(err=>{
console.log(err.message);
})
}
// the function is for search method
// upon search, this function is called and the state of the pools is changed
searchTrigger = (search) => {
Search = search.toLowerCase();
SearchList = this.state.Pools.filter((e)=> {
if (e.name.toLowerCase().includes(Search)){
this.setState({
loading: false
})
return e
}
})
if (SearchList.length === 0){
this.setState({
loading: true,
message: "No pools found"
})
}
}
render() {
return (
<div>
<Searchbar trigger={this.searchTrigger}/>
{ this.state.loading ?
<div className="d-flex justify-content-center">{this.state.message}</div>
:<div>
{Search === "" ? <Card1 pools={this.state.Pools}/> : <Card1 pools={SearchList}/> }
</div>
}
</div>
)
}
}
The function searchTrigger is passed to another class component called Searchbar which basically displays the search bar. Upon searching something, the function searchTrigger is called and the searched value is passed as an argument to this function.
So, I am trying to test this function and I am new to react and testing. I found some examples online and tried a simple testing whether the function is called or not. My CardMain.test.js code looks like this:
describe("callback function test", ()=> {
it("runs it", () => {
//const spy = jest.spyOn(CardMain.prototype,"searchTrigger");
const cardmain = shallow(<CardMain/>)
const spy = jest.spyOn(cardmain.instance(), "searchTrigger");
expect(spy).toHaveBeenCalled()
})
});
I get the TypeError: Cannot read property 'get' of undefined pointing to the axios.get("/pools") in the CardMain component inside componentDidMount. axios is being imported from another component api.js which creates the instance of axios using axios.create. I have no idea what the problem is. I am very new to react. I have absolutely no idea, how do I test these components? Could somebody help me?
Update:
So, i tried mocking axios call:
let Wrapper;
beforeEach(() => {
Wrapper = shallow( <CardMain/>);
});
describe("Card Main", ()=> {
it("returns data when called", done => {
let mock = new MockAdapter(axios);
const data = [{
name: "Test",
response: true
}];
mock.onGet('My_URL')
.reply(200,data);
const instance = Wrapper.instance();
instance.componentDidMount().then(response => {
expect(response).toEqual(data);
done();
});
});
});
It says "cannot read property .then of undefined"
I am developing a react native project.
I am first in React Native.
I have some errors in my project.
I 'd like to know how to handle error in React native.
And how can I see the errors?
if ((this.state.loadedUrl === 'https://www.truthbaron.com/') && (!this.state.newsflag)){
const html = event.nativeEvent.data;
const $ = CheerIO.load(html);
isLoggedIn = Object.keys($(PROFILE_SELECTOR)).includes('0');
if (isLoggedIn) {
if(this.state.messagesflag){
profileLink = $(PROFILE_SELECTOR).eq(0).children().attr('href');
username = profileLink.match(/members\/[a-z]+/)[0].slice(8);
if (username.endsWith('/')) username = username.slice(0, username.length - 1);
this.setState({ url: `${profileLink}messages`, loading: true });
console.log('messages page:' + profileLink);
}else{
profileLink = $(PROFILE_SELECTOR).eq(0).children().attr('href');
console.log('profile page!!!' + profileLink);
this.setState({ url: profileLink, loading: true });
}
}
else {
const loginLink = $(LOGIN_SELECTOR).eq(0).children().attr('href');
console.log('loginLink:' + loginLink);
this.setState({ url: loginLink, loading: true });
}
}
There are 2 ways to handle errors.
1.try {
var test;
test.color;
} catch(err) {
// handle error here
}
2.const previousHandler = ErrorUtils.getGlobalHandler();
ErrorUtils.setGlobalHandler((error, isFatal) => {
// handle the error here
console.log(error);
});
And you can monitor the errors in React Native with Rollbar.
For further more information, you can visit this URL.
https://rollbar.com/blog/react-native-error-monitoring/
There is multiple ways to handle error in react, and it is based on the architecture or code you building.
A standard way might be
try {
//code blocks
catch {
// error .log
}
You can relay on method function such as
console.error
If you are making api request
class IsLoading extends React.Component {
constructor(props) {
super(props);
// initialise our state
this.state = { isLoading: false };
}
componentDidCatch(error, info) {
// if we have a promise then we can deal with it
if(error instanceof Promise) {
// we have a promise so lets update our state
this.setState({ isLoading: true });
// once the promise has resolved we can update our state again and grab the data
error.then((data) => this.setState({ isLoading: false, data }));
}
}
render() {
// as props.children is a function, let's invoke it and p ass in out state
return this.props.children(this.state) }
}
}
const Loader = props => (
<IsLoading>
// this is the function that gets called in the render met hod above
{({isLoading, data}) => (
isLoading
// show some loading text if we're loading
? "Loading..."
// copy our children and pass in the data as a prop :
React.cloneElement(props.children, {data})
)}
</IsLoading>
);
I am having a ReactJS component which does two things:
- on ComponentDidMount it will retrieve a list of entries
- on Button click it will submit the select entry to a backend
The problem is that i need to mock both requests (made with fetch) in order to test it properly. In my current testcase i want to test a failure in the submit on the button click. However due some odd reason the setState is triggered however the update from that is received after i want to compare it.
Dumps i did for the test. First one is the state as listen in the test. The second is from the code itself where it is setting state().error to the error received from the call
FAIL react/src/components/Authentication/DealerSelection.test.jsx (6.689s)
● Console
console.log react/src/components/Authentication/DealerSelection.test.jsx:114
{ loading: true,
error: null,
options: [ { key: 22, value: 22, text: 'Stationstraat 5' } ] }
console.log react/src/components/Authentication/DealerSelection.jsx:52
set error to: my error
The actual test code:
it('throws error message when dealer submit fails', done => {
const mockComponentDidMount = Promise.resolve(
new Response(JSON.stringify({"data":[{"key":22,"value":"Stationstraat 5"}],"default":22}), {
status: 200,
headers: { 'content-type': 'application/json' }
})
);
const mockButtonClickFetchError = Promise.reject(new Error('my error'));
jest.spyOn(global, 'fetch').mockImplementation(() => mockComponentDidMount);
const element = mount(<DealerSelection />);
process.nextTick(() => {
jest.spyOn(global, 'fetch').mockImplementation(() => mockButtonClickFetchError);
const button = element.find('button');
button.simulate('click');
process.nextTick(() => {
console.log(element.state()); // state.error null even though it is set with setState but arrives just after this log statement
global.fetch.mockClear();
done();
});
});
});
This is the component that i actually use:
import React, { Component } from 'react';
import { Form, Header, Select, Button, Banner } from '#omnius/react-ui-elements';
import ClientError from '../../Error/ClientError';
import { fetchBackend } from './service';
import 'whatwg-fetch';
import './DealerSelection.scss';
class DealerSelection extends Component {
state = {
loading: true,
error: null,
dealer: '',
options: []
}
componentDidMount() {
document.title = "Select dealer";
fetchBackend(
'/agent/account/dealerlist',
{},
this.onDealerListSuccessHandler,
this.onFetchErrorHandler
);
}
onDealerListSuccessHandler = json => {
const options = json.data.map((item) => {
return {
key: item.key,
value: item.key,
text: item.value
};
});
this.setState({
loading: false,
options,
dealer: json.default
});
}
onFetchErrorHandler = err => {
if (err instanceof ClientError) {
err.response.json().then(data => {
this.setState({
error: data.error,
loading: false
});
});
} else {
console.log('set error to', err.message);
this.setState({
error: err.message,
loading: false
});
}
}
onSubmitHandler = () => {
const { dealer } = this.state;
this.setState({
loading: true,
error: null
});
fetchBackend(
'/agent/account/dealerPost',
{
dealer
},
this.onDealerSelectSuccessHandler,
this.onFetchErrorHandler
);
}
onDealerSelectSuccessHandler = json => {
if (!json.error) {
window.location = json.redirect; // Refresh to return back to MVC
}
this.setState({
error: json.error
});
}
onChangeHandler = (event, key) => {
this.setState({
dealer: event.target.value
});
}
render() {
const { loading, error, dealer, options } = this.state;
const errorBanner = error ? <Banner type='error' text={error} /> : null;
return (
<div className='dealerselection'>
<Form>
<Header as="h1">Dealer selection</Header>
{ errorBanner }
<Select
label='My dealer'
fluid
defaultValue={dealer}
onChange={this.onChangeHandler}
maxHeight={5}
options={options}
/>
<Button
primary
fluid
onClick={this.onSubmitHandler}
loading={loading}
>Select dealer</Button>
</Form>
</div>
);
}
}
export default DealerSelection;
Interesting, this one took a little while to chase down.
Relevant parts from the Node.js doc on Event Loop, Timers, and process.nextTick():
process.nextTick() is not technically part of the event loop. Instead, the nextTickQueue will be processed after the current operation is completed, regardless of the current phase of the event loop.
...any time you call process.nextTick() in a given phase, all callbacks passed to process.nextTick() will be resolved before the event loop continues.
In other words, Node starts processing the nextTickQueue once the current operation is completed, and it will continue until the queue is empty before continuing with the event loop.
This means that if process.nextTick() is called while the nextTickQueue is processing, the callback is added to the queue and it will be processed before the event loop continues.
The doc warns:
This can create some bad situations because it allows you to "starve" your I/O by making recursive process.nextTick() calls, which prevents the event loop from reaching the poll phase.
...and as it turns out you can starve your Promise callbacks as well:
test('Promise and process.nextTick order', done => {
const order = [];
Promise.resolve().then(() => { order.push('2') });
process.nextTick(() => {
Promise.resolve().then(() => { order.push('7') });
order.push('3'); // this runs while processing the nextTickQueue...
process.nextTick(() => {
order.push('4'); // ...so all of these...
process.nextTick(() => {
order.push('5'); // ...get processed...
process.nextTick(() => {
order.push('6'); // ...before the event loop continues...
});
});
});
});
order.push('1');
setTimeout(() => {
expect(order).toEqual(['1','2','3','4','5','6','7']); // ...and 7 gets added last
done();
}, 0);
});
So in this case the nested process.nextTick() callback that logs element.state() ends up running before the Promise callbacks that would set state.error to 'my error'.
It is because of this that the doc recommends the following:
We recommend developers use setImmediate() in all cases because it's easier to reason about
If you change your process.nextTick calls to setImmediate (and create your fetch mocks as functions so Promise.reject() doesn't run immediately and cause an error) then your test should work as expected:
it('throws error message when dealer submit fails', done => {
const mockComponentDidMount = () => Promise.resolve(
new Response(JSON.stringify({"data":[{"key":22,"value":"Stationstraat 5"}],"default":22}), {
status: 200,
headers: { 'content-type': 'application/json' }
})
);
const mockButtonClickFetchError = () => Promise.reject(new Error('my error'));
jest.spyOn(global, 'fetch').mockImplementation(mockComponentDidMount);
const element = mount(<DealerSelection />);
setImmediate(() => {
jest.spyOn(global, 'fetch').mockImplementation(mockButtonClickFetchError);
const button = element.find('button');
button.simulate('click');
setImmediate(() => {
console.log(element.state()); // state.error is 'my error'
global.fetch.mockClear();
done();
});
});
});
There are several asynchronous calls required to update the state, so your process.nextTick() isn't sufficient. To update the state, this needs to happen:
your test code clicks, and the event handler callback is queued
the event handler callback runs, runs fetch, gets a promise rejection, and runs the error handler
the error handler runs setState, which queues the state update (setState is asynchronous!)
your test code runs, checking the element's state
the state update runs
In short, you need to wait longer before asserting on the state.
A useful idiom to "wait" without nested process.nextTick() calls is to define a test helper
function wait() {
return new Promise((resolve) => setTimeout(resolve));
}
and then do
await wait();
as many times as required in your test code. Note that this requires you to define test functions as
test(async () => {
})
rather than
test(done => {
})