RN - Using props within style declaration - reactjs

I'd like to use a prop value to add color dynamically - relevant to the parent view.
I have the following -
<Text style={[copyStyles.copyrightTxt, copyStyles.copyColor{this.Props.Color}]}>© Copyright 2019 LRM Security Ltd</Text>
which would be styled via:
const copyStyles = StyleSheet.create({
copyColorOrange:{
color:'#f79431',
}
});
But I get an error 'unexpected token' - can anyone tell me where i'm going wrong please?

You can access properties of objects in two ways:
objectName.propertyName
or you can use the alternative way that allows you to use strings
objectName["propertyName"]
You should use the alternative way to access the value inside the styles object by constructing the string that you need.
copyStyles[`copyColor${this.props.Color}`]
also isn't props meant to be all lowercase?

Better way is to nest your property:
const copyStyles = {
copyColor:{
Orange:{
color:'#f79431',
}
}
};
And use : copyStyles.copyColor[this.props.Color]

Related

Cannot assign to read only property of Object in TypeScript

can anyone explain to me please why and how this might happen:
I have a typescript app with Zustand state management.
Somewhere during the app I am updating certain elements by extracting them from the state and cloning via simple Object.Assign :
let elemToUpdate = Object.assign({},story?.content?.elementsData[nodeId]);
console.log(elemToUpdate);
if(elemToUpdate) {
if(elemToUpdate.title) elemToUpdate.title[editorLang] = newName;
else elemToUpdate.title = {[editorLang]:newName} as TextDictionary;
updateElement(nodeId,elemToUpdate);
}
Now the interesting part is on my first try the update goes through without fail, but the next object I am trying to update fails with the following message:
Tree.tsx:39 Uncaught TypeError: Cannot assign to read only property 'en' of object '#<Object>'
I can't understand WHY the first one comes through, but the second gets blocked.
(I know HOW to fix it, need to do deep clone, I just want to understand WHY)
Thanks
First, let's start from why some objects in your code are readonly. Based on what you described in the question, you use a Zustand state manager. Such managers traditionally wraps you stored data to readonly objects to prevent it's manual mutation (expecting, you will change the state only via built-in mechanisms), to guarantee data stability. So, if the story?.content?.elementsData[nodeId] is the Zustand state object, it self and all it's nested objects are converted to readonly.
Second, let's define, which objects will be blocked. I see at least two objects here: elemToUpdate: { ..., title: { [lang]: string }} (elemToUpdate and it's title). Both will be converted to readonly.
Third, you use Object.assign({}, ...) which creates a new object (new reference) and clones all properties of the source object. It happens only for first level of properties, no deep clone. So, as the title is a reference to another object, it will be cloned as is and in the new object it still leads to the existing { [lang]: string } object. There are several way to solve that: 1) deep clone as you mentioned; 2) manually clone title property, for instance {..., title: { ... elemToUpdate.title }} or via Object.assign
But I would suggest don't mutate you object this way. Probably, your entire algorithm has some architectural issues in general.
That is expected because in the first case you are not assigning value to the title you are only changing the value of the title property. In the second case, you are reassigning the value of the title property,
it's the read-only value you cant change it. Let's understand with a simple example
Javascript: Only for example not related to problem
const user = {
name: 'John',
}
user.name = "Pete"; // This works
const user = {
name: 'John',
}
user = { name: 'Pete'} // This doesn't work
Typescript:
const user: Readonly<{
a: {
name: string
}
}> = {
a:{ name: 'John',}
}
user.a.name = "Pete"; // This works
user.a = { name: 'John',} // not work
The same is happening there, Typescript does not check deep Readonly prop. check here

React-table: how to use getCellProps, getHeaderProps etc.?

The documentation of React Table is really sparse and I can't make sense of the examples.
For example here: https://react-table.tanstack.com/docs/examples/data-driven-classes-and-styles, we see this code:
{row.cells.map(cell => {
return (
<td
// Return an array of prop objects and react-table will merge them appropriately
{...cell.getCellProps([
{
className: cell.column.className,
style: cell.column.style,
},
getColumnProps(cell.column),
getCellProps(cell),
])}
>
{cell.render('Cell')}
</td>
It's not clear to me what's happening.
GetCellProps is called, and provided an array as argument. This array contains 1. an object with two properties, and 2. a call to getColumnProps (what does this do?), and then 3. another call to getCellProps but now with the cell as an argument.
The result of this call is then operated on with the spread-operator (...).
If anyone can help me understand all of this, much appreciated.
I will try to explain this to you -
cell.getCellProps -> is the method exposed on each cell by react-table, this is pretty useful in getting all props which the given cell will require based on the plugins used.
Few eg. of same are -
https://github.com/TanStack/react-table/blob/76a4a861ee56b782404ef91987c3b5691ecf2ebc/src/hooks/useTable.js#L415
https://github.com/TanStack/react-table/blob/76a4a861ee56b782404ef91987c3b5691ecf2ebc/src/plugin-hooks/useFlexLayout.js#L47
https://github.com/TanStack/react-table/blob/76a4a861ee56b782404ef91987c3b5691ecf2ebc/src/plugin-hooks/useAbsoluteLayout.js#L23
Now this method can expect an object or list of object to be provided as argument, which mainly acts as merge/override on prior values, loosely similar to object.assign with some exceptions based on key name, for more details on merge logic refer - https://github.com/TanStack/react-table/blob/76a4a861ee56b782404ef91987c3b5691ecf2ebc/src/publicUtils.js#L19
Below code is helpful in adding className in cell and merge/override styles with previously returned values.
{
className: cell.column.className,
style: cell.column.style,
}
Below two methods are props provided by component
getColumnProps(cell.column),
getCellProps(cell)
getColumnProps={column => ({
onClick: () => alert('Column!'),
})}
getCellProps={cellInfo => ({
style: {
backgroundColor: `hsl(${120 * ((120 - cellInfo.value) / 120) * -1 +
120}, 100%, 67%)`,
},
}
So whatever is returned by these props that will actually get merged in cell props, and will be applied to td.
P.S. - Refer https://github.com/tannerlinsley/react-table/blob/master/src/publicUtils.js#L48 for what all types of argumants can be expected in cell.getCellProps.

TypeScript: how to send array as a prop?

I am sending an array of data to a component as a prop, like:
<InfoTable TableInfo={tableRows} />;
Where tableRows is the array.
On my InfoTable component file I define my props like
interface InfoTableProps {
tableInfo: TableInfo[];
}
Which allows me to .map() through the tableInfo array, eg:
let tableRow = tableInfo.map(function(tableInfoRow) {
// Do some stuff
}
This works fine. However, my compiler gets a warning on tableInfo: TableInfo[];
Cannot find name 'TableInfo'. TS2304
I've tried Googling the problem of course but I just get people asking the same question.
Would anyone know how to remove this error or what it means?
Don't you need to define the TableInfo type somewhere?
eg
Interface TableInfo {
id: number
name: string
}
Sorry If you've already done that and its something else :-)

How does exportFunction work in Turbotable?

How do we implement the exportFunction in the parameters of Turbotable.
According to the definition of PrimeNg exportFunction: A function to implement custom export. Need to return string value.
Is there anyone who has already used this feature in his chart and if so how?
you can use like this;
component;
exportFunction = (data,field) =>{
console.log(data,field)
}
html;
<p-table ... [exportFunction]="exportFunction">

Find element by id in react-testing-library

I'm using react-testing-libarary to test my react application. For some reason, I need to be able to find the element by id and not data-testid. There is no way to achieve this in the documentation.
Is there a way to achieve this?
I have the rendered output as follows:
const dom = render(<App />);
I'm looking for something along the lines of:
const input = dom.getElementById('firstinput');
//or
const input = dom.getById('firstinput');
I feel like none of the answers really gave a complete solution, so here it is:
const result = render(<SomeComponent />);
const someElement = result.container.querySelector('#some-id');
I found a way to do this.
import App from './App';
import { render, queryByAttribute } from 'react-testing-library';
const getById = queryByAttribute.bind(null, 'id');
const dom = render(<App />);
const table = getById(dom.container, 'directory-table');
I hope this helps.
It looks you have DOM node itself as a container. Therefore, you should be able to call .querySelector('#firstinput') with that.
There are two ways to do so
Simply use container.getElementById('id'). In the end, all the helpers are doing is making queries like this one under the hood
If you want to have your custom query you can write a custom render. Check the documentation for more info https://github.com/kentcdodds/react-testing-library#getbytestidtext-textmatch-htmlelement
As a final note, if you can avoid looking for elements by id it's better.
You can set up with testIdAttribute in the configuration.
configure({ testIdAttribute: 'id' })
https://testing-library.com/docs/dom-testing-library/api-configuration
The setting has pros and cons. The benefit of it is that you can set an id for multiple uses. (Test id, marketing analytics, tag manager, ...etc) You don't have to add both id and test-id. It's good for the conciseness of the code.
But be careful, you might accidentally set the same id at two different components on the same page. Remember to add index or identification to a component id for list items.
My advice: stop adding and searching by ids, this always takes to much time and effort because you have to add the ids (sometimes test-ids) and then find out the best way to query the element. But even if you really need an id, this tool will save you a lot of time by showing the best way to query any DOM element on your screen: Testing Playground
If you use TypeScript, and want to get a non-null result, here's a convenience function:
function getById<T extends Element>(container: HTMLElement, id: string): T {
const element = container.querySelector<T>(`#${id}`);
assert(element !== null, `Unable to find an element with ID #${id}.`)
return element;
}
You can then use it like this:
import { render } from '#testing-library/react';
const { container } = render(<App />);
const myInputElement = getById<HTMLInputElement>(container, 'myInputElement');

Resources