Lets say I am using a <FormattedNumber > that I am importing from react-intl
It has a property called minimumSignificantDigits, so that if I set it all my numbers look awesome like 1.00... great when you are working with currencies.. so I can use it like this:
<FormattedNumber minimumSignificantDigits={3} value={somevar}>
I have about 100 of these on the page and I don't want to keep setting this minimumSignificantDigits property on every single on of them, and then when the client changes his/her mind I have to update all of them.
Is there any way that I can set/override some default properties on that component when I import it.
Obviously yes, make a wrapper around <FormattedNumber>
// TreeCharFormattedNumber.jsx
export default TreeCharFormattedNumber = ({ value }) => (
<FormattedNumber minimumSignificantDigits={3} value={value}>>
);
// YourComponent.jsx
import TreeCharFormattedNumber from "./TreeCharFormattedNumber";
...
<div>
<TreeCharFormattedNumber value={myAwsomeValue} />
</div>
...
You can also put TreeCharFormattedNumber in the same file leaving export default
Wrap the imported component with another.
In this example, the default value for minimumSignificantDigits would be 3 with any other props passed through as is. (This allows you to also override your default on a per component basis if required)
function FormattedNumberWithDefault(props) {
return (
<FormattedNumber minimumSignificantDigits={3} {...props}>
)
}
Wrap it with your own component:
export const MyFormattedNumber = (props) => (
<FormattedNumber minimumSignificantDigits={3} {...props}>
);
Now you can import it whenever it's needed, and everything you'll pass to MyFormattedNumber will be passed to the wrapped FormattedNumber:
<MyFormattedNumber value={3} />
You can easily override the default if you pass the property minimumSignificantDigits, because spreading the props can replace the default prop as well:
<MyFormattedNumber minimumSignificantDigits={15} value={somevar}>
I have found that the following also works:
import React from 'react'
import {FormattedNumber} from 'react-intl'
import {Link} from 'react-router-dom'
FormattedNumber.defaultProps = {
style: 'decimal',
minimumFractionDigits: 2,
maximumFractionDigits: 2,
}
Related
In my project, i'm using "react-three/fiber" which is required to store all content inside "Canvas".
Also, for storing some states and share it with other modules in "Canvas" i'm using recoil's atom.
So, inside "RecoilRoot" everything works great, BUT i need to show data from atom in the "UI" component.
I can't put "RecoilRoot" out of "Canvas", so i tried to add a second "RecoilRoot". It kinda does the trick (now i can use "useRecoilValue" in "UI"), but it shows only default state of atom...
Here is how my "App.js" looks like:
function App() {
return (
<Suspense fallback={<Loading />}>
{/*Second RecoilRoot, outside the Canvas*/}
<RecoilRoot>
<Canvas shadows>
<GameSettingsProvider>
{/*Main RecoilRoot with data*/}
<RecoilRoot>
<ObjectShow />
{/*Main RecoilRoot with data*/}
</RecoilRoot>
</GameSettingsProvider>
</Canvas>
<UI />
{/*Second RecoilRoot, outside the Canvas*/}
</RecoilRoot>
</Suspense>
);
}
export default App;
Atom with data:
import {atom} from "recoil";
export const scoreState = atom({
key: "score", // unique ID (with respect to other atoms/selectors)
default: 0, // default value (aka initial value)
});
UI module:
import React from 'react'
import { useRecoilValue } from 'recoil'
import { scoreState } from './gameState'
function UI() {
const score = useRecoilValue(scoreState);
return (
<div className='score-title'>
<h1 className='score-title__text'>
Score: {score}
</h1>
</div>
)
}
export default UI
So, how can i use atom values outside "RecoilRoot" or, at least, share from nested one?
There are 2 different renders DOM and Canvas.
Couldn't find any workarounds to transfer states via Recoil, so i used Zustand state manager for score state.
It can transfer states even between renders!
Work like a charm.
I have 3 tsx (jsx) files in my react app:
My index.tsx:
import ReactDOM from 'react-dom';
import Page from './Page';
ReactDOM.render(
<Page />,
document.getElementById('root')
);
Then my Page.tsx (which I render in index ↑):
import React from "react";
import Menu from "./Menu";
export default (props: any) => {
let rf = React.useRef<HTMLDivElement>();
return (
<Menu ref={rf}>
</Menu>
)
}
And my Menu.tsx, which use forwardRef:
import React from "react"
export default React.forwardRef((props, ref:any) => {
return (
<div ref={ref}>
</div>
)
})
Problem is, that it gives me error in Page.tsx (in VS code is underlined opening Menu tag):
Type '{ children: never[]; ref: MutableRefObject<HTMLDivElement |
undefined>; }' is not assignable to type 'IntrinsicAttributes &
RefAttributes'.
Property 'children' does not exist on type
'IntrinsicAttributes & RefAttributes'.ts(2322)
When I remove enclosing tag from Menu (change it to self-closing element), it works:
import React from "react";
import Menu from "./Menu";
export default (props: any) => {
let rf = React.useRef<HTMLDivElement>();
return (
<Menu ref={rf} />
)
}
But I want (need) i to be paired (in order to write children element to it).
How can I solve it?
Thanks! :)
<Tag/> means no children.
<Tag></Tag> means takes children.
So your error is basically telling you that your trying to send children, but your <Menu/> component doesn't say it takes any.
The solution, react.forwardRef takes two generic arguments, the first is the type of DOM element your will be forwarding the ref too, and the second is what props it takes.
So using this info we should be able to convert your example by doing ->
import React from "react"
export default React.forwardRef<HTMLDivElement,{children: React.ReactNode}>(
(props, ref) => {
return (
<div ref={ref}>
{props.children}
</div>
)
})
You will also notice I have also taken out the any on the ref, this will now be properly typed.
Your other problem is also in how your sending the ref, you will get 'MutableRefObject<HTMLDivElement | undefined>',
This is because you have not supplied a value to React.useRef, so indeed the value can be HTMLDivElement or undefined, and that's what the error says.
Simple solution just set the default to null, and TS will be happy. This works because MutableRefObject<T | null>, and null is actually a valid React component, basically acts like a No op render.
eg.
let rf = React.useRef<HTMLDivElement>(null);
Here is a working TS snippet.. TS Playground
I have stored the data in a file (details.js) as a JavaScript Object, then I'm trying to pass it as a prop from MainComponent to ChildComponent. Well the prop is accessible in ChildComponent but I could access it with props.ChildDetails[0].name instead of props.name directly as shown in the Official documentation
What am i missing?
details.js
export const DETAILS=[
{
id:0,
profile:'/assests/images/john.png',
name:'John Doe',
}
]
MainComponent.js
import React,{useState} from 'react';
import Child from './ChildComponent';
import {DETAILS} from '../Shared/details';
function MainComponent(){
return(
<>
<Child ChildDetails={DETAILS}/>
</>
);
}
export default MainComponent;
ChildComponent.js
import React from 'react';
function ChildComponent(props){
console.log(props.name) //Output:undefined
console.log(props.ChildDetails[0].name) //Output:John Doe
return(
<div></div>
);
}
export default ChildComponent;
Because Details is object.
Try something like:
function ChildComponent({ ChildDetails }){
const { name } = ChildDetails[0]
...
}
That's not even React. That's JavaScript
Edit. Oops. People said that's an array. I thought it's just an object. Fixed code a bit
here is where you passed the object
<Child ChildDetails={DETAILS}/>
and here is how you tried to use it
console.log(props.name) //Output:undefined
since you defined the name as ChildDetails there is no such thing as props.name instead you must say
console.log(props.ChildDetails)
and if you want to get access to an name you should declare the index if not react does not know which index you want to use, so as you did before
props.ChildDetails[0].name
I'm trying to figure out how to set the initial state in my React app inside an arrow function. I've found the example here: https://reactjs.org/docs/hooks-state.html but it's not helping me a lot. I want to put tempOrders and cols into the state so my other components have access to them and can change them.
Here is my code:
// creating tempOrders array and cols array above this
const App = () => {
const [orders, setOrders] = useState(tempOrders);
const [columns, setColumns] = useState(cols);
return (
<div className={'App'}>
<Schedule
orders={orders}
setOrders={setOrders}
columns={columns}
setColumns={setColumns}
/>
</div>
);
};
export default App;
Now my other related question is if I don't pass in those 4 variables/functions into Schedule, ESLint complains to me about them being unused variables in the 2 const lines above. I wouldn't think I would need to pass them in because that is the whole point of state, you just have access to them without needing to pass them around.
You should always keep the state at the top-level component where it needs to be accessed. In this case you should define the state in the Schedule-Component since it's not used anywhere else.
If you have a more complex hierachy of components and want to create a shared state (or make a state globally accessible) I would suggest following thump rule:
For small to medium sized apps use the context-API with the useContext-hook (https://reactjs.org/docs/hooks-reference.html#usecontext). It's fairly enough for most cases.
For large apps use redux. Redux needs a lot of boilerplate and adds complexity to your app (especially with typescript), which is often not required for smaller apps. Keep in mind that redux is not a replacement for thecontext-API. They work well in conjunction and can/should be used together.
EDIT
Simple example for useContext:
ScheduleContext.js
import React from "react";
export const ScheduleContext = React.createContext();
App.jsx
import {ScheduleContext} from "./ScheduleContext";
const App = () => {
const [orders, setOrders] = useState(tempOrders);
const [columns, setColumns] = useState(cols);
const contextValue = {orders, setOrders, columsn, setColumns};
return (
<div className={'App'}>
<ScheduleContext.Provider value={contextValue}>
<Schedule/>
</ScheduleContext.Provider>
</div>
);
};
export default App;
You can now use the context in any component which is a child of the <ScheduleContext.Provider>.
Schedule.jsx
import React, {useContext} from "react";
import {ScheduleContext} from "./ScheduleContext";
const Schedule = () => {
const {orders, setOrders, columsn, setColumns} = useContext(ScheduleContext);
// now you can use it like
console.log(orders)
return (...)
}
Note that you could als provide the context inside the <Schedule>-component instead of <App>.
I wrote this from my head, but it should work. At least you should get the idea.
it seems you want the child component "Schedule" have to change the father's state...... is correct?
so you can try to write like this example:
import React, {useState} from 'react';
import './App.css';
function Test(props){
const{setCount,count}=props
return(
<div>
<h1>hello</h1>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
)
}
function App() {
// Declare a new state variable, which we'll call "count"
const [count, setCount] = useState(0);
return (
<div>
<Test
setCount={setCount}
count={count}
/>
{count}
</div>
);
}
export default App;
https://repl.it/#matteo1976/ImperfectYawningQuotes
Where my Test would work as your Schedule
I would like to use a cellRenderer in React for most of my columns. So, in my colDefs, I have an additional field called unit. If the unit exists, I am trying to have a heatMap like color grid which is handled in my TableCell React component. This identical react component has worked in other data grids like ZippyUI. Can the cellRenderer function return a React component, which is a virtual DOM object, or does it have to be a true HTML DOM object? Would doing something like this with ag-Grid's cellRenderer component method be preferable?
colDefs.map((x) => {
if (x.hasOwnProperty('unit')) {
x.cellRenderer = (params) => {
return <TableCell value={params.value} units={x.unit} min={min[x.field]} max={max[x.field]} colorScheme={colorScheme} />;
};
}
});
For React, instead of using cellRenderer, cellEditor, etc, you want to use cellRendererFramework, cellEditorFramework and pass in a class that has setUp and render methods.
I figured out the implementation that is required and figured I'd leave this question up for future users. It seems that the component approach is required, and you can use the cellRendererParms to pass in additional arguments. Then, within the actual renderer (ReactTableCellRenderer below), you can access them via this.props.params.units (or replace units with whatever other param you pass in the object).
colDefs.map((x) => {
if (x.hasOwnProperty('unit')) {
x.cellRenderer = reactCellRendererFactory(ReactTableCellRenderer);
x.cellRendererParams = {units: x.unit, min: min[x.field], max: max[x.field], colorScheme: colorScheme};
}
});
The params passed via cellRendererParams are then available within the React component (in my case, ReactTableCellRenderer) via this.props.params.units (or substitute appropriate vairable name for units)
Update 2021
Complementing #Tom answer. You want to use cellRendererFramework in the columns definition. Here's an example:
import * as React from "react";
import { FontIcon } from "#fluentui/react";
import { ColDef, ColGroupDef } from "ag-grid-community";
const isFavoriteCellRenderer = (params: any) => {
if (params.value) {
return (
<div>
<FontIcon
iconName="FavoriteStarFill"
title="Favorite"
aria-label="Favorite"
/>
</div>
);
}
return (
<div>
<FontIcon
iconName="FavoriteStar"
title="Not favorite"
aria-label="Not favorite"
/>
</div>
);
};
export const getIsFavoriteColumn = (): ColDef | ColGroupDef => ({
headerName: "isFavorite",
field: "isFavorite",
cellRendererFramework: isFavoriteCellRenderer,
});
Note params.value is boolean, this could be any type depending on your row model.
Cell renderer docs for react grid