"Cannot read property 'nationalCode' of undefined" error in redux-form - reactjs

I use redux-form for my reactjs applicaion, in validate.js file when i want validate my formSection "personFields" code will occur with "Cannot read property 'nationalCode' of undefined" error
// validate.js file
export default values => {
const errors = {};
errors.personFields = validatePersonFields(values.personFields);
return errors
};
const validatePersonFields = values => {
const errors = {};
if (!values.nationalCode) {
errors.nationalCode = 'it is required';
}
return errors;
};
how can I solve this problem?

values.personFields must be undefined:
const validatePersonFields = (values = {}) => {
const errors = {};
if (!values.nationalCode) {
errors.nationalCode = 'it is required;
}
return errors;
};
This will at least allow you to continue when no field values are present.

Related

Web worker causes a gradual increase of memory usage! how to use transferable objects?

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)));

Cannot read properties of undefined (sub-schema not working) : Mosh React Course

Learning React from Code with Mosh with Typescript got stuck in Form Section
Getting error on validateProperty function
validate = () => {
let options = {abortEarly:false}
const {error} = Joi.validate(this.state.data, this.schema, options);
if (!error) return null;
const errors : any = {};
for (let item of error.details)
errors[item.path[0]] = item.message;
return errors;
}
validateProperty = ({name, value} : HTMLInputElement) => {
const obj = { [name] : value };
// **getting error in below line**
const schema = { [name] : this.schema[name] }
const { error } = Joi.validate(obj, schema);
return (error) ? error.details[0].message : null;
}
I also did try devtarek solution but not worked
I did find the joi-browser version 13.4 is outdated that's why unable to access property like this - said by someone on github (follow above link)
Please let me know if any additional details required. I will update it. TThanks

Redux Saga All effects proper usage

I've started working with Redux-saga and I've been following the advanced section for composing sagas in parallel.
Currently this is the function that I'm using with all method, I need these to filter and transform some pages metadata before assigning to the Nav component.
function* CreateNavFromPages(action) {
const menu = action.payload.menu
const menuPagesData = toList(menu.get('pages')).map(
page => {
const title = page.get('title');
const uri = page.get('uri');
return toPageData(title, uri);
}
);
const staticPagesData = menuPagesData.filter(p => p.get('kind') === 'static');
const pages = yield all(
staticPagesData.map(page => backend(PagesApi, page.get('id')))
);
console.log(pages);
const staticPagesMetadata = pages
.filter(({ result, err }) => !err && result.items.length > 0)
.map(result => fromJS(buildMetadata(fromJs(result.items[0]))));
const pagesMetadata = menuPagesData.map(page => {
const key = staticPagesMetadata.findIndex(el => findPage(el, id));
if (key) {
return staticPagesMetadata[key].copy({data: page.get('data')});
}
return page;
});
const nav = Immutable.Map({
position: 'top',
target: 'home',
pagesMetadata
});
const {result, err} = yield backend(Navs.create, nav);
if (err) { ...}
else {
yield put(NavActions.createNavSuccessful(result.nav));
return true;
}
}
function findPage(el, id) {
return el.get('id') === id;
}
backend.js
export default function* backend(...args) {
try {
return { result: yield call(...args)};
}
catch (e) {...}
}
pages_api.js
.....
function get(id) {
return fetch('/api/pages/${id}');
}
export default {get}
When I run this on my App, I get the following error:
uncaught at watchMany
at takeLatest(CREATE_NAV, createNavFromPages)
at createNavFromPages
TypeError: pages.filter is not a function
When checking console.log, I see the following:
{size: 2, _origin: 0, _capacity: 2, _level: 5, _root: null, _tail: {Array(2){Generator, Generator}}}
Is there something I'm missing here?
UPDATED: Added PagesApi definition
As the error suggests the problem is here:
const pages = yield all(
staticPagesData.map(page => backend(PagesApi, page.get('id')))
);
The all effect expects a native javascript array as input parameter, however you are passing down Immutable list (which is the return type of staticPagesData.map).
Since the all effect doesn't understand Immutable.js lists it treats it as any other object - that is it resolves to the object itself, which is what you see in your console.log message.
To fix it you can e.g. convert the immutable list to javascript array like so:
const pages = yield all(
staticPagesData.map(page => backend(PagesApi, page.get('id'))).toJS()
);

Exposing error values to component in Redux Form

I have a component that uses redux form and validates like so:
const validate = values => {
// Initialize errors constant.
const errors = {};
const testEmail = value =>
value && !/^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(value) ?
errors.email = 'Invalid email address' : undefined;
// Only run test if email exists.
testEmail(values.email);
// Show error if value is touched and not filled out.
const required = ['email', 'team', 'gameID', 'season', 'winner', 'bet', 'contractName'];
required.map( input => {
if (!values[input]) {
return errors[input] = 'Required'
};
return null;
});
return errors;
}
const SportsFormWithCSS = CSSModules(SportsForm, styles, { allowMultiple: true });
SportsForm = reduxForm({
form: 'SportsForm',
validate
})(SportsFormWithCSS);
Is there a way I can add the error values to props, for instance? I have tried a couple implementations with little luck.

React Redux and side effects explanation

I'm wrapping my forms to provide automatic validation (I don't want to use redux-form).
I want to pass an onSubmit handler which must be fired after every input in form is validated: but how do I wait for form.valid property to turn into true && after wrapping submit was fired? I'm missing some logic here!
//in my Form.js hoc wrapping the forms
#autobind
submit(event) {
event.preventDefault();
this.props.dispatch(syncValidateForm({ formName: this.props.formName, form: this.props.form }));
// ==> what should I do here? Here I know submit button was pressed but state is not updated yet with last dispatch result reduced!
//if(this.props.form.valid)
// this.props.submit();
}
render() {
return (
<form name={this.props.formName} onSubmit={this.submit}>
{ this.props.children }
</form>
);
//action.js validating given input
export const syncValidateInput = ({ formName, input, name, value }) => {
let errors = {<computed object with errors>};
return { type: INPUT_ERRORS, formName, input, name, value: errors };
};
//action.js validating every input in the form
export const syncValidateForm = ({ formName, form }) => {
return dispatch => {
for(let name in form.inputs) {
let input = form.inputs[name];
dispatch(syncValidateInput({ formName, input, name: input.name, value: input.value }));
}
};
};
//in my reducer I have
case INPUT_ERRORS:
let errors = value;
let valid = true;
let errorText = '';
_.each(errors, (value, key) => {
if(value) {
valid = false;
errorText = `${errorText}${key}\n`;
}
});
form.inputs[name].errors = errors;
form.inputs[name].valid = valid;
form.inputs[name].errorText = errorText;
_.each(form.inputs, (input) => form.valid = !!(form.valid && input.valid));
return state;
Help!
Depending on your build config you could use Async/Await for your submit function. Something like
async submit(event) {
event.preventDefault();
const actionResponse = await this.props.dispatch(syncValidateForm({ formName: this.props.formName, form: this.props.form }));
if (actionResponse && this.props.form.valid) { //for example
// finish submission
}
}
And I think you will need to update your syncValidateForm slightly but this should put you on the right path.

Resources