DateInput default parse function - reactjs

I wanted to wrap DateInput into MyDateInput with pre-filled parse function argument in order not to repeat parse={dateParser} everywhere.
So I created such element:
import React from 'react';
import { DateInput } from 'admin-on-rest';
import moment from 'moment';
const dateParser = date => {
// v is a `Date` object
if (!(date instanceof Date) || isNaN(date)) return;
return moment(date).format('YYYY-MM-DD');
};
const MyDateInput = (props) => {
return (
<DateInput {...props} parse={dateParser} />
);
};
Which did not work as I expected, so from docs I found that I have to do something like this, but source argument not parsed as it has to be:
import { Field } from 'redux-form';
const MyDateInput = (props) => {
return (
<Field name='date' component={DateInput} {...props} parse={dateParser} />
);
};
When I put 2 such inputs into one form it result in merge of input for both of them.
Is there a way to it properly?

Redux Form name prop sets the name of the field in the state that the Field Component will affect.
So you need to give a different name prop every time you want to use the Field component.
You can provide name as a prop from the form that is calling the Wrapped DateInput comp make sure it is unique ever time.

Related

pass arguments like onChange and onClick to a react component with typescript

I'm trying to create a page with react, for that I'm using typescript.
In the project there is a component that is an input and in it I want there to be an onChange function, but when I put it in the component, it has a red dash, indicating that there is an error, how could I pass the onChange correctly? I know how to do it using javascript, but not with typescript.
I made the code as follows:
import { StyledInput } from "./Input.styles";
export const Input = ( { onChange } ) => {
return(
<StyledInput onChange={onChange}/>
)
}
You need to define the prop type, e.g.
export const Input = ( { onChange }:{ onChange:(x:string)=>void } ) => {
Or more precisely
type Props ={ onChange:React.ComponentProps<typeof<StyledInput>>['onChange'] }
export const Input = ( { onChange }:Props ) => {
This approach ensures that if you're 3rd party package type signature changes you still have the same precise type defined in your props

How can I make a native app that will pick and design charts according to the name fetched from API?

I want my TabDashboardDetails.js to find out which chart to be displayed according to the name of the chart fetched from API. In TabDashboardDetails.js I want to replace CogniAreaChart with a component that will have specific view for fetched chart and can also take data from API.
Here is my TabDashboardDetails.js
import React from 'react';
import DefaultScrollView from '../components/default/DefaultScrollView';
import ChartView from '../components/default/ChartView';
import CogniAreaChart from '../components/CogniAreaChart';
import { mapNameToChart } from '../utils/commonFunctions';
import { areaChartData } from '../chartData';
const TabDashboardDetail = ({ navigation, route }) => {
const tabsConfig = route.params.tabsConfig;
const ChartToDispay = mapNameToChart();
return (
<DefaultScrollView>
{tabsConfig.components.map((comp) => {
console.log(tabsConfig.components);
return (
<ChartView key={comp.name} title={comp.name}>
<CogniAreaChart
name={comp.name}
areaChartData={areaChartData}
height={200}
/>
</ChartView>
);
})}
</DefaultScrollView>
);
};
export default TabDashboardDetail;
I want to pick charts from commonfunctions.js that I have used:
/* eslint-disable prettier/prettier */
import {
AreaChart,
BarChart,
LineChart,
PieChart,
SingleCircularProgress,
Histogram,
SimpleTable,
BubbleChart,
CandlestickChart,
SankeyChart,
ScatterPlot,
StackedBarChart,
WaterfallChart,
TreeMap,
MixAndMatch,
SimpleCard,
BlogTable,
LiquidTable,
} from 'react-native-svg-charts';
export const mapNameToChart = (name) => {
const nameToChart = {
AreaChart: AreaChart,
BarGraph: BarChart,
LineChart: LineChart,
PieChart: PieChart,
SingleCircularProgress: SingleCircularProgress,
Histogram: Histogram,
SimpleTable: SimpleTable,
BubbleChart: BubbleChart,
CandlestickChart: CandlestickChart,
SankeyChart: SankeyChart,
ScatterPlot: ScatterPlot,
StackedBarGraph: StackedBarChart,
WaterfallTable: WaterfallChart,
TreeMap: TreeMap,
MixAndMatch: MixAndMatch,
SimpleCard: SimpleCard,
BlogCard: BlogTable,
LiquidGauge: LiquidTable,
};
return nameToChart[name];
};
You first need to import all the chart types in the file containing mapNameToChart and map the name to the Chart type accordingly. Then You can try this
const ChartToDispay = mapNameToChart(name);
<ChartToDisplay {...your_props_here} />
In your mapNameToChart function it looks like AreaChart is an actual component and the rest are just string names of components instead of the components themselves. You want to change it so that all of the entries in the map are the components. You want mapNameToChart(name) to return a callable component. Then you can call that component with your props.
I'm not fully understanding your the API comes into play here, but it seems like we get the props by looking up the name? I don't know the the API data comes from, so I'm expecting the components array to be passed as a prop to the CustomChart.
const CustomChart = ({name, components, ...props}) => {
// get the component function/class from your map
const Component = mapNameToChart(chart);
// find the component configuration from your API
const config = components.find(obj => obj.name === name);
// call with props from the config and passed down props
return (
<Component
{...config}
{...props}
/>
)
}

How can I change the state of one React component based on the value of its sibling?

I'm attempting to build a form in React that has some auto-filling features. I'm trying to create the ability to 'lock' the value of LossOver50k to either 'Yes' or 'No' based on the ReplacementCost value to prevent input errors. The code I have right now is
import React, { useState } from "react";
import ReplacementCost from "./ReplacementCost";
import LossOver50K from "./LossOver50K";
const LossAmount = () => {
const [replacementCost, setReplacementCost] = useState("");
const [highValue, setHighValue] = useState("No");
const handleChange = (newReplacementCost) => {
setReplacementCost(newReplacementCost);
if (replacementCost >= 50000) {
setHighValue("Yes");
}
};
// const changeHighValue = (newHighValue) => {
// setHighValue(newHighValue);
// };
return (
<div>
<ReplacementCost value={replacementCost} onChange={handleChange} />
<LossOver50K value={highValue} />
</div>
);
};
export default LossAmount;
My commented piece was working as an onChange handler on the LossOver50k component but I'm trying to manipulate the value of LossOver50k if the value of Replacement Cost is equal to or greater than 50,000. I have each of these components successfully updating their state when I input the values directly by passing the props down to the child components. Any ideas?
TIA!
Your condition should be with the most updated value and not with a staled one (as setState is async):
const handleChange = (newReplacementCost) => {
setReplacementCost(newReplacementCost);
if (newReplacementCost >= 50000) {
setHighValue("Yes");
}
};

Field/Input label based on record in react admin

I'd like part of the record to be included in the label for a BooleanField (and BooleanInput). I'm trying to use WithProps to accomplish this.
If I use
<BooleanField source="FileSystem" label="FileSystem" />
This seems to work just fine. If, instead I try to wrap it
const makeLabel = (props)=>{
let label = `Filesystem for ${props.record.id}`;
return {label};
}
const withLabel = withProps(makeLabel);
const BooleanFieldWithLabel = compose(withLabel)((props)=>{
console.log("props after compose",props);
return <BooleanField {...props}/>
});
And then use <BooleanFieldWithLabel source="FileSystem" /> It doesn't render any label. I've tried a few different ways and nothing seems to work even though I can see in the console.log that the correct label is in props. What am I doing wrong here?
I have the same question, I cannot display the label base on field's value on "Show" page.
From react-admin source code, it seems only I set "addLabel" prop on the direct child of "SimpleShowLayout" or "TabbedShowLayout", then I can see label on my custom field.
But it is not configurable, I want to show/hide label base on field's value. Do I need to implement my own custom "SimpleShowLayout" or "TabbedShowLayout"? Or is there any better approaches?
Update my post.
I just figure out the solution by implementing an HOC like below. I am wondering is there any better approaches to implement the same feature?
import React from "react";
import get from "lodash/get";
import { TextField, DateField, Labeled } from "react-admin";
const NullableField = WrappedComponent => props => {
const { record, source } = props;
const value = get(record, source);
return value ? (
<Labeled {...props}>
<WrappedComponent {...props} />
</Labeled>
) : null;
};
const NullableTextField = NullableField(TextField);
const NullableDateField = NullableField(DateField);
export { NullableTextField, NullableDateField };

How to generate unique IDs for form labels in React?

I have form elements with labels and I want to have unique IDs to link labels to elements with htmlFor attribute. Something like this:
React.createClass({
render() {
const id = ???;
return (
<label htmlFor={id}>My label</label>
<input id={id} type="text"/>
);
}
});
I used to generate IDs based on this._rootNodeID but it’s unavailable since React 0.13. What is the best and/or simplest way to do it now?
The id should be placed inside of componentWillMount (update for 2018) constructor, not render. Putting it in render will re-generate new ids unnecessarily.
If you're using underscore or lodash, there is a uniqueId function, so your resulting code should be something like:
constructor(props) {
super(props);
this.id = _.uniqueId("prefix-");
}
render() {
const id = this.id;
return (
<div>
<input id={id} type="checkbox" />
<label htmlFor={id}>label</label>
</div>
);
}
2019 Hooks update:
import React, { useState } from 'react';
import _uniqueId from 'lodash/uniqueId';
const MyComponent = (props) => {
// id will be set once when the component initially renders, but never again
// (unless you assigned and called the second argument of the tuple)
const [id] = useState(_uniqueId('prefix-'));
return (
<div>
<input id={id} type="checkbox" />
<label htmlFor={id}>label</label>
</div>
);
}
This solutions works fine for me.
utils/newid.js:
let lastId = 0;
export default function(prefix='id') {
lastId++;
return `${prefix}${lastId}`;
}
And I can use it like this:
import newId from '../utils/newid';
React.createClass({
componentWillMount() {
this.id = newId();
},
render() {
return (
<label htmlFor={this.id}>My label</label>
<input id={this.id} type="text"/>
);
}
});
But it won’t work in isomorphic apps.
Added 17.08.2015. Instead of custom newId function you can use uniqueId from lodash.
Updated 28.01.2016. It’s better to generate ID in componentWillMount.
Update React 18
React 18 has introduced a new hook which generates a unique ID:
const id = useId();
Hook API docs: https://reactjs.org/docs/hooks-reference.html#useid
From your example, you could call the hook inside a component:
import React, { useId } from 'react'
function TextField = (props) => {
// generate unique ID
const id = useId();
return (
<>
<label htmlFor={id}>My label</label>
<input id={id} type="text"/>
</>
);
}
Following up as of 2019-04-04, this seems to be able to be accomplished with the React Hooks' useState:
import React, { useState } from 'react'
import uniqueId from 'lodash/utility/uniqueId'
const Field = props => {
const [ id ] = useState(uniqueId('myprefix-'))
return (
<div>
<label htmlFor={id}>{props.label}</label>
<input id={id} type="text"/>
</div>
)
}
export default Field
As I understand it, you ignore the second array item in the array destructuring that would allow you to update id, and now you've got a value that won't be updated again for the life of the component.
The value of id will be myprefix-<n> where <n> is an incremental integer value returned from uniqueId. If that's not unique enough for you, consider making your own like
const uniqueId = (prefix = 'id-') =>
prefix + Math.random().toString(16).slice(-4)
There are also hundreds or thousands of other unique ID things out there, but lodash's uniqueId with a prefix should be enough to get the job done.
Update 2019-07-10
Thanks to #Huong Hk for pointing me to hooks lazy initial state, the sum of which is that you can pass a function to useState that will only be run on the initial mount.
// before
const [ id ] = useState(uniqueId('myprefix-'))
// after
const [ id ] = useState(() => uniqueId('myprefix-'))
You could use a library such as node-uuid for this to make sure you get unique ids.
Install using:
npm install node-uuid --save
Then in your react component add the following:
import {default as UUID} from "node-uuid";
import {default as React} from "react";
export default class MyComponent extends React.Component {
componentWillMount() {
this.id = UUID.v4();
},
render() {
return (
<div>
<label htmlFor={this.id}>My label</label>
<input id={this.id} type="text"/>
</div>
);
}
}
Extending #forivall's comment
If the whole goal is to link up a <label> and <input> elements and they don't depend on props, then instead of using auto generated unique id's, the most optimal and performant approach would be to use useRef.
useRef returns a mutable ref object whose .current property is initialized to the passed argument (initialValue). The returned object will persist for the full lifetime of the component.
Meaning, you can use useRef to mimic instance variables which is not recomputed on props changes. useRef is not only used to reference a DOM element.
Example using an external random ID generator (e.g. loadash)
import React, { useRef } from 'react'
import uniqueId from 'lodash/utility/uniqueId'
function InputField = (props) => {
const {current: fieldId} = useRef(uniqueId('prefix-'))
return (
<div>
<input id={fieldId} type="checkbox" />
<label htmlFor={fieldId}>label</label>
</div>
);
}
Example using a simple custom random ID generator
import React, { useRef } from 'react'
function InputField = (props) => {
const {current: fieldId} = useRef("prefix-" + (Math.random().toString(36)+'00000000000000000').slice(2, 7))
return (
<div>
<input id={fieldId} type="checkbox" />
<label htmlFor={fieldId}>label</label>
</div>
);
}
Explanation:
The above random ID (Math.random().toString(36)+'00000000000000000').slice(2, 7) comes from this stackoverflow answer and will always guarantee 5 characters, compared to Math.random().toString(16).slice(-4) which may return empty strings.
Also, it's important to use a prefix where the prefix must start with a letter ([A-Za-z]) in order for it to be a valid HTML4 id attribute value.
A version without Lodash using hooks:
function useUniqueId() {
const [id] = useState(() => `component-${Math.random().toString(16).slice(2)}`)
return id
}
Hopefully this is helpful to anyone coming looking for a universal/isomorphic solution, since the checksum issue is what led me here in the first place.
As said above, I've created a simple utility to sequentially create a new id. Since the IDs keep incrementing on the server, and start over from 0 in the client, I decided to reset the increment each the SSR starts.
// utility to generate ids
let current = 0
export default function generateId (prefix) {
return `${prefix || 'id'}-${current++}`
}
export function resetIdCounter () { current = 0 }
And then in the root component's constructor or componentWillMount, call the reset. This essentially resets the JS scope for the server in each server render. In the client it doesn't (and shouldn't) have any effect.
I create a uniqueId generator module (Typescript):
const uniqueId = ((): ((prefix: string) => string) => {
let counter = 0;
return (prefix: string): string => `${prefix}${++counter}`;
})();
export default uniqueId;
And use top module to generate unique ids:
import React, { FC, ReactElement } from 'react'
import uniqueId from '../../modules/uniqueId';
const Component: FC = (): ReactElement => {
const [inputId] = useState(uniqueId('input-'));
return (
<label htmlFor={inputId}>
<span>text</span>
<input id={inputId} type="text" />
</label>
);
};
The useId hook will replace the unstable useOpaqueIdentifier in an upcoming stable version of React (already available in the latest React alphas). It will generate stable ids during server rendering and hydration to avoid mismatches.
For the usual usages of label and input, it's just easier to wrap input into a label like this:
import React from 'react'
const Field = props => (
<label>
<span>{props.label}</span>
<input type="text"/>
</label>
)
It's also makes it possible in checkboxes/radiobuttons to apply padding to root element and still getting feedback of click on input.
I found an easy solution like this:
class ToggleSwitch extends Component {
static id;
constructor(props) {
super(props);
if (typeof ToggleSwitch.id === 'undefined') {
ToggleSwitch.id = 0;
} else {
ToggleSwitch.id += 1;
}
this.id = ToggleSwitch.id;
}
render() {
return (
<input id={`prefix-${this.id}`} />
);
}
}
Don't use IDs at all if you don't need to, instead wrap the input in a label like this:
<label>
My Label
<input type="text"/>
</label>
Then you won't need to worry about unique IDs.

Resources