flow issue I can't overcome. Passing props buy not confirming to shape - flowtype

I have a parent component, this a very scaled down version.
I define type State (as example shows)..... and then I pass down a portion of (their values) them into a child component. That child component (in a seperate file) again, defines its props.... but I am getting errors for every single prop in the parent. infuriating.
for each prop being passed to the "MyChildComponent", I am getting this error:
props of React element MyChildComponent. This type is incompatible with object type
type State = {
name: string,
age: number,
shoe: number,
hair: string
}
class Dude extends component<void, Props, State> {
props: Props;
state: State;
// these values get over-written by some ajax call
constructor() {
this.state = {
name: 'george',
age: 999,
hair: 'brown',
shoe: 11
}
displaySomeComponent = (): React.Element<*> => {
const { name, age, hair } = this.state;
return (
// EACH ONE OF THESE GIVES AN ERROR
//Flow: props of React element `MyChildComponent`. This type is incompatible with object type
<MyChildComponent
name={name},
age={age},
hair={hair}
/>
)
}
}
// In a separate file for MyChildComponent
// No errors in this file
type Props = {
name: string,
age: number,
hair: string
}
class MyChildComponent extends component<void, Props, void> {
props: Props;
render() {
// all renders fine
}
}
I am at my wits end. I do not understand what is going on, as I've passed things into many child components, but this is mucking up. I am thinking it might have something to do with the destructuring?

Hmm, it's hard to say. If I fix a few syntax errors, this seems to work fine in Flow 0.54: my version
To track down what is wrong, you might try adding type declarations to see where things go wrong. Like, inside the parent render method, under the destructuring, try adding name: string; and see if Flow complains about that.

Related

Value of type '(params: any) => CSSProperties' has no properties in common with type 'Properties<string | number>'. Did you mean to call it?

Why does this property in react CSS not work if it is of type CSSProperties? How can I get it to work with Properties<string | number> ?
export const fields: GridFieldsConfiguration[] = [
{
...defaultColDefs,
field: 'amInitials',
displayNameRule: 'Asset Manager',
flex: 1.1,
minWidth: 75,
cellStyle: (params: any): any => {
getCellStyle(params, 'amInactive')
}
}
];
const isDisabledbStyle = {
color: '#FF0000'
};
const getCellStyle = ((params: any, inactiveCol: string): CSSProperties => {
console.log(params);
if (params?.api?.getValue(inactiveCol, params.node) === true) {
return isDisabledbStyle;
} else {
return isDisabledbStyle;
}
}
);
Here are the types. cellStyle comes from CSSProperties which is an extension of CSS.Properties<string | number>.
export interface GridFieldConfiguration extends FieldConfiguration {
cellStyle?: CSSProperties;
}
export interface CSSProperties extends CSS.Properties<string | number> {
/**
* The index signature was removed to enable closed typing for style
* using CSSType. You're able to use type assertion or module augmentation
* to add properties or an index signature of your own.
*
* For examples and more information, visit:
* https://github.com/frenic/csstype#what-should-i-do-when-i-get-type-errors
*/
}
Here is Properties
export interface Properties<TLength = string | 0> extends StandardProperties<TLength>, VendorProperties<TLength>, ObsoleteProperties<TLength>, SvgProperties<TLength> {}
Sounds like you are using AG Grid, and trying to configure a cellStyleFunc for the cellStyle option of column definitions?
As shown in the linked documentation, it is indeed possible to provide a function that takes a params argument.
But it looks like in your case, you have an intermediate GridFieldsConfiguration custom type, that expects cellStyle to be of type Properties<string | number> (which is very probably actually React.CSSProperties), which does not accepts the function form, hence the error message.
If the rest of your code that handles GridFieldsConfiguration really expects CSSProperties and not the function form, then you would have to refactor it first, so that it can handle that form.
If all it does is to pass the cellStyle option to AG Grid, then you just need to improve the definition of GridFieldsConfiguration type. You can re-use the actual types grom AG Grid, e.g.:
import { AgGridColumnProps as ColDef } from "ag-grid-react";
export interface GridFieldsConfiguration {
cellStyle?: ColDef["cellStyle"];
}
But note that CSSProperties is actually not type-compatible with cellStyle. To fix it, simply remove the return type assertion on your getCellStyle function. If you want to ensure that the returned objects still resembles a CSS object, you can use the new satisfies operator:
const isDisabledbStyle = {
color: '#FF0000'
} satisfies CSSProperties;
const getCellStyle = (params: any, inactiveCol: string) => {
return isDisabledbStyle;
};
Playground Link

stenciljs: Issue with slot

I have created a simple web (stencil) component AuthGuard, not to be be confused with Angular's AuthGuard.
The purpose of this component is to check if the user is logged in.
If yes, render the slot html.
If not, render the Signup button.
The component code is as follows:
import { Component, Host, h } from '#stencil/core';
import { Build, State } from '#stencil/core';
import { AuthService } from 'auth/auth.service';
import { ConfigService } from 'common/config.service';
#Component({
tag : 'auth-guard',
styleUrl : 'auth-guard.css',
shadow : true,
})
export class AuthGuard {
#State() canRender : boolean = false;
componentWillLoad() {
if (Build.isBrowser) {
const timerId = setInterval(() => {
if (AuthService.isInitialized) {
AuthService.vol$.subscribe(_u => {
this.canRender= true;
});
clearInterval(timerId);
}
}, ConfigService.loadTime);
}
}
render() {
console.log('auth guard :: render', this.canRender, AuthService.me);
return (
<Host>
{
this.canRender ? (
AuthService.me && AuthService.me.id.length > 0 ? (
<slot></slot>
) : (
<ion-button
href="/signup"
routerDirection="forward"
color="danger">
Signup
</ion-button>
)
): null
}
</Host>
);
}
}
Now in the other file, I use the following code:
<auth-guard slot='end'>
<volunteer-mini volunteer={AuthService.me}></volunteer-mini>
</auth-guard>
With this what I am expecting is
Nothing to be rendered, till this.canRender becomes true.
Once this.canRender becomes true, If AuthService.me is valid, render the slot HTML,
If AuthService.me is null, render signup button.
But seems when this.canRender is false, it tried to render volunteer-mini the slot HTML, which is a problem. Since volunteer-mini internally depends on AuthService.me, which is not yet initialized.
But once this.canRender becomes true, other 2 scenarios are working fine.
It's in general a bad idea to write an auth-guard using stencil. The core problem is that your slot exists before your component has initialized.
Therefor, using your current code, you'd have to manually remove the slot after you decided that you don't have the rights.
Additionally, if you do not define a slot position, but still provide a slot-content in your parent, it will still be appended to your inner children.
To resolve this problem, you can refactor your component to an function, like <Host>, but this has other pits to consider.

redux: how to deal with an API that returns null for some things?

If I am interacting with an API that returns null for some objects that may or may not have value, how can I reconcile that with the reducers?
example: app state
{
foo: {
foo1: null,
foo2: {
bar: null,
bar2: null
}
}
}
but the server, when things are null, returns this:
{
foo: null
}
but it can return the full state when things have value:
{
foo: {
foo1: "somefoo,
foo2: {
bar: "barvalue,
bar2: 27
}
}
}
the problem I ham having is that my reducers are trying to load the state from the return value from the server, and then my components are trying to read from a null object and it is failing.
EDIT: the reducer and the component would look like this... so the component is trying to read some nested json, which may come back as unnreadable because the parent object is null. In this case I know I could hack up a solution that checks if the object is null and inserts my predefined initial state...
BUT...my actual example is a bigger json object than this and I know it will change in the future, so I need a solution that is not so fragile and cumbersome as adding a ton of logic here to check to make sure that every object down the nested like is not null.
var updateSettings = (settings = jsonShape, action) => {
swtich (action.type) {
case UPDATE_SETTINGS:
return Object.assign({}), settings, {
foo2: {
...settings.foo2,
bar: action.newBar
}
}
}
}
const Component = ({ settings }) => {
return (
<div>{ settings.foo2.bar }</div>
)
}

Selecting a Row in React Griddle, and changing tr background color

I'm just wondering if someone has already been able to change the color of a row, in React Griddle, by clicking on it (just once).
I'm experimenting things with JQuery, and even with Griddle Metadata, but it may be done in a cleaner way ?
Edit : I'm using React 15, Griddle inside MantraJS/Meteor, getting the data in my react Component using a Mantra container.
I can get the data by using onClick event, but not able to switch the background color in the onClick event, or playing with Metadatas.
Thanks !
EDIT : I use another view to display the content of the table, so for now I don't need to change the background of my tables cells, but if I found a solution I'll complete this post
You can use react-griddle props rowMetadata and onRowClick to do this:
class ComponentWithGriddle extends React.Component {
constructor(props) {
super(props);
this.state = {
selectedRowId: 0,
};
}
onRowClick(row) {
this.setState({ selectedRowId: row.props.data.id });
}
render() {
const rowMetadata = {
bodyCssClassName: rowData => (rowData.id === this.state.selectedRowId ? 'selected' : ''),
};
return (
<Griddle
...
rowMetadata={rowMetadata}
onRowClick={this.onRowClick.bind(this)}
/>
);
}
}
Now this adds a selected class to the selected <tr> elements, so you can use custom styles to add colors or whatever styles you want to apply to the selected row.
Note that a more convenient API for selecting rows has been called for in the Griddle Github issues.
For whatever reason, I couldn't get Waiski's answer to work for me at all. I'm assuming that something must have changed in Griddle over the past two years. It looks like the current prevailing advice on the Web is to "implement row selection as a plugin", but I couldn't find any examples of that either. After a long hard look at the code for the Position plugin’s TableEnhancer on GitHub and a bunch of trial and error I eventually managed to cobble together the following row selection plugin for Griddle in TypeScript:
import * as React from "react";
import * as Redux from "redux";
import Griddle, { connect, GriddlePlugin, components } from "griddle-react";
export type RowId = string | number;
export type RowClickHandler = (event: React.MouseEvent<Element>, rowId: RowId) => void;
export type RowIdGetter<TData> = (rowData: TData) => RowId;
export interface IRowEnhancerProps {
rowClickHandler: RowClickHandler;
rowId: RowId;
isSelected: boolean;
}
export class RowSelector<TData> {
private _rowClickHandler: RowClickHandler = null;
private _rowIdGetter: RowIdGetter<TData>;
constructor(rowClickHandler: RowClickHandler, rowIdGetter: (rowData: TData) => RowId) {
this._rowClickHandler = rowClickHandler;
this._rowIdGetter = rowIdGetter;
}
public rowIdToSelect: RowId;
public plugin: GriddlePlugin = {
components: {
RowEnhancer: (OriginalComponent: React.ComponentClass<components.RowProps>) =>
this.rowSelectionEnhancer(OriginalComponent)
}
}
private rowSelectionEnhancer(
OriginalComponent: React.ComponentClass<components.RowProps>
): React.ComponentClass<components.RowProps> {
const rowDataSelector = (state, { griddleKey }) => {
return state
.get('data')
.find(rowMap => rowMap.get('griddleKey') === griddleKey)
.toJSON();
};
return Redux.compose(
connect((state, props) => {
const rowData: TData = rowDataSelector(state, props as { griddleKey });
const rowId: RowId = this._rowIdGetter(rowData);
return {
...props,
rowClickHandler: this._rowClickHandler,
rowId: rowId,
isSelected: rowId.toString() === this.rowIdToSelect.toString()
};
})
)(class extends React.Component<IRowEnhancerProps, any>{
public render() {
return (
<OriginalComponent
{...this.props}
onClick={(event) => this.props.rowClickHandler(event, this.props.rowId)}
className={this.props.isSelected ? "selected" : ""}
/>
);
}
});
}
}
Here's a rough outline of how it's used by a component. (Note that I had to selectively extract this example from a much larger and more complicated component, so there might be some errors/inconsistencies; sorry about that. It should still give a good overall idea of the approach.)
import * as React from "react";
import Griddle, { RowDefinition, plugins, GriddlePlugin} from "griddle-react";
import * as MyGriddlePlugins from "../GriddlePlugins";
export interface IPartInfo {
serialNumber: number,
name: string,
location: string
}
export interface IPartListProps{
parts: IPartInfo[],
selectedSerialNumber: number
}
export class PartList extends React.Component<IPartListProps, void > {
private rowSelector: MyGriddlePlugins.RowSelector<IPartInfo>;
private rowIdGetter: MyGriddlePlugins.RowIdGetter<IPartInfo>;
constructor(props?: IPartListProps, context?: any) {
super(props, context);
this._rowClickHandler = this._rowClickHandler.bind(this);
this.rowSelector = new MyGriddlePlugins.RowSelector(
this._rowClickHandler,
this._rowIdGetter);
}
private _rowClickHandler: MyGriddlePlugins.RowClickHandler =
(event: React.MouseEvent<Element>, selectedSerialNumber: MyGriddlePlugins.RowId) => {
if (selectedSerialNumber !== this.props.selectedSerialNumber) {
/*
Set state, dispatch an action, do whatever. The main point is that you
now have the actual event from the click on the row and the id value from
your data in a function on your component. If you can trigger another
render pass from here and set a fresh value for this.rowSelector.rowIdToSelect
then the "selected" CSS class will be applied to whatever row this click
event just came form so you can style it however you like.
*/
}
}
private _rowIdGetter: (rowData: IPartInfo) => MyGriddlePlugins.RowId =
(rowData: IPartInfo) => rowData.serialNumber;
public render(): JSX.Element {
this.rowSelector.rowIdToSelect = this.props.selectedSerialNumber;
return (
<div>
<Griddle
data={this.props.parts}
plugins={[plugins.LocalPlugin, this.rowSelector.plugin]}
>
<RowDefinition>
<ColumnDefinition id="name" title="Part Name" />
<ColumnDefinition id="location" title="Installed Location" />
<ColumnDefinition id="serailNumber" title="Serial Number" />
</RowDefinition>
</Griddle>
</div>
);
}
}
So, what's actually going on here? The component creates an instance of the plugin class at instantiation time, passing in an event handler to capture the click on the row and an accessor function to retrieve your ID value (not an inscrutable internal ID) from a row of your data. Just before the component returns its rendering, a value is set on the component's instance of the plugin, that way, when Griddle renders the plugin has the data to figure out when it's on a selected row and then adjust the CSS accordingly. The handler function from your component is then assigned to the row's onClick handler so your component can get the data from the click and do whatever it needs to do.
This passes the "It works for me" test (on React 15.6) which in my case is a straightforward master/detail view driven by a traditional table implemented through Griddle. I have no idea how well it would work with some of Griddle's more advanced features.

Using bracket notation on redux reducer

I have a form that should update my store for every change done in the store. To not have to make 20-30 different actions for each property/field, i though creating one single action containing property-name and value would be a good approach. So the action looks like this:
export function(property, value){
return{
type: UPDATE_MODEL,
property: property,
value: value
}
}
When passing that into my reducer, i tried using the bracket notation.
case UPDATE_MODEL: {
return Object.assign({}, state, {[action.property]: action.value});
}
When doing this:
dispatch(myAction("Type", {Id: 1}));
My reducer will set the Type property to an array instead of just setting the property Type to the incoming object. Is there a way to rig this properly?
I tried to use _cloneDeep from lodash on the current state object i wanted to change, then used the bracket notation on that, and then returning object.assign with that new object, that actually works, it just feels very wrong though.
How about this?
function updateModel(state, action) {
return Object.assign({}, state, {
[action.property]: action.value
})
}
function updateModelAction(property, value) {
return {
type: 'UPDATE_MODEL',
property,
value
}
}
function reducer(state, action) {
switch (action.type) {
case 'UPDATE_MODEL':
return updateModel(state, action)
default:
return state
}
}
console.log(reducer({}, updateModelAction('foo', { bar: 'baz' })))

Resources