Field/Input label based on record in react admin - reactjs

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

Related

React Element not re-rendering on setState when state is passed

I'm trying to implement a simple dark/light theme toggle to my website. In my base App.tsx I've implemented the state I use:
const [colorScheme, setColorScheme] = useState("light");
I pass that "colorScheme" variable as a prop to every other element. The theme toggle is contained in a header element, so I also pass the "setColorScheme" function to header as a prop. Within Header.tsx, the code triggered when the toggle is clicked is:
setColorScheme(s => s === "dark" ? "light" : "dark");
Within every specific element, I set the color scheme like so:
<ElementName className={"element_name element_name_"+colorScheme}/>
I have all the css for styling the component contained in the class "element_name", and then all relevant color data is contained in "element_name_light" or "element_name_dark".
When the toggle in the header is clicked, a re-render is triggered for the main body of the app, and for the header. But all of the other elements do not re-render. If I navigate to another element, the re-render happens and the color scheme appears as intended.
Attached is a gif of this happening.
I'm still learning React, so I'm sure it's something obvious I'm missing. I would appreciate any tips anyone can provide! Thanks
One note: I am using react functionally, rather than implementing classes for each component.
It's impossible to tell exactly what mistake you made since you haven't shared your code. But I can tell you the root mistake is not using React's context API. This will allow you to hold the color scheme and the toggle function as a global state and import them into every component via the useContext hook.
Here's an example on stackblitz: https://stackblitz.com/edit/react-ts-lhwstv?file=color-scheme-ctx.tsx
Here's the docs: https://reactjs.org/docs/context.html
Note: I'm using typescript, if you're using plain javascript just remove the type declarations and the generic typings <Type>.
You start by creating the context and giving a default value:
type ColorScheme = 'light' | 'dark';
type Props = { colorScheme: ColorScheme; toggleColorScheme: () => void };
export const ColorSchemeCtx = createContext<Props>({
colorScheme: 'light',
toggleColorScheme: () => {},
});
I like to then create a provider component for organization.
export const ColorSchemeCtxProvider: FC<PropsWithChildren<{}>> = ({
children,
}) => {
const [colorScheme, setColorScheme] = useState<ColorScheme>('light');
function toggleColorScheme() {
setColorScheme((s) => (s === 'dark' ? 'light' : 'dark'));
}
return (
<ColorSchemeCtx.Provider value={{ colorScheme, toggleColorScheme }}>
{children}
</ColorSchemeCtx.Provider>
);
};
Then wrap all components that need the context - probably just put it at the highest level possible.
root.render(
<StrictMode>
<ColorSchemeCtxProvider>
<App />
</ColorSchemeCtxProvider>
</StrictMode>
);
Now any component can get both the color scheme and / or the toggle function with useContext
export default function App() {
const { colorScheme, toggleColorScheme } = useContext(ColorSchemeCtx);
return (
<div>
<p>The color scheme is: {colorScheme}</p>
<button onClick={toggleColorScheme}>TOGGLE</button>
<CompOne />
<CompTwo />
<CompThree />
</div>
);
}
export default function CompOne() {
const { colorScheme } = useContext(ColorSchemeCtx);
return <div className={'comp-one ' + colorScheme}></div>;
}

Return value from react component

I am developing a react app, and I must to build a custmon dropdonw component, I need that my component returns the selected value.
I know the starndad way (or I think is the best way but, may be better if you know)
const getSelectedValue = (value) => {
console.log(value);
}
const component = <Dropdonw valueList={list} getSelectedValue={getSelectedValue} />
Is there any way to return de value using a variable or the javascript way component.options[e.selectedIndex].value. For example
const component = <Dropdonw valueList={list} />
...
<code>
...
console.log(component.getSelectedValue());
Could you tell me if there are others ways to get or return values, or the standard and best way y passing a function?
You can't return a single value from a react component. What you can do is lift the state up:
Parent:
const [dropdownVal, setDropdownVal] = useState(null)
<MyDropdown valueList={list} setDropdownVal={setDropdownVal} dropdownVal={dropdownVal} />
MyDropdown:
export const MyDropdown = ({valueList, dropdownVal, setDropdownVal}} => {
return(
<Dropdown valueList={valueList} value={dropdownVal} onChange={/* get value and call setDropdownVal */} />
)
}

Reset value of RangePicker Ant design

I'm using RangePicker from ant design in my app. I'va faced with following issue: when to click on button "reset" I can't clear the value in RangePicker component. Please help me how to do it? I've tried assign defaultValue but It doesn't work.
Use the value parameter of RangePicker instead.
Something like this should probably work. Wasnt in a position to make a enviroment and test it but hopefully you get the idea. Ask any questions if theres something that is not clear.
const SomeComponent = props => {
const [fromDate, setFromDate] = useState('2000-01-01');
const [toDate, setToDate] = useState('2000-01-02');
const onChangeDateRange = (dates, datesString) => {
setFromDate(datesString[0]);
setFromDate(datesString[1]);
//More code
}
const onResetClick = () => {
setFromDate('2000-01-01');
setToDate('2000-01-02');
}
return (
<>
<RangePicker
onChange={onChangeDateRange}
value={[moment(fromDate, dateFormat), moment(toDate, dateFormat)]}
/>
<Button onClick={onResetClick}>Reset</Button
</>
)
}
The docs on ant design are very good and at the end you can find all availible parameters a component can have. DatePicker e.g. has a allowClear. https://ant.design/components/date-picker/

Using Styled Components in functional React components

(I hope and assume that there is a simple answer to this question, but I could not find it in the docs)
I've just converted a React project to use Styled Components, and love the DRYness and reusability. But, I've not yet figured out the syntax for using styled components in existing functional components that do other work too.
Here's one example:
const StyledSearchBarPane = styled(Pane)`
grid-area: search-bar;
`;
const SearchBarPane = () => {
const {query} = useContext(panelContext);
let [newQuery, setNewQuery] = useState(query);
return (
<StyledSearchBarPane>
<Bar>
<SearchInput newQuery={newQuery} setNewQuery={setNewQuery}/>
</Bar>
</StyledSearchBarPane>
);
};
How can I avoid naming StyledSearchBarPane? It's only used once -- in SearchBarPane -- and I'd rather it were simply part of the latter's definition.
Yes you can.
Basically what you need to do is use your component as you would normally do and if you want to use a local style on the component without creating one you can use a Wrapper like you did.
But the change is, instead of exporting your component you export the wrapper. Then your component will receive as props className and you need to put it where you want to apply thoses style, in the example since it's globally you put in the root element.
If you don't put the className props it won't style anything.
const SearchBarPane = ({className}) => {
const {query} = useContext(panelContext);
let [newQuery, setNewQuery] = useState(query);
return (
<div className={className}>
<Bar>
<SearchInput newQuery={newQuery} setNewQuery={setNewQuery}/>
</Bar>
</div>
);
};
const StyledSearchBarPane = styled(SearchBarPane)`
grid-area: search-bar;
`;
export default StyledSearchBarPane;
https://styled-components.com/docs/advanced

Wrapping Antd Component

I want to wrap antd component eg. Input into MyInput so I can add support for new pros and classNames, however it stoped working when I put then inside of the Form
MyInput.js
import { Input } from 'antd';
function MyInput({ className='', ...rest }) {
const computedClassName = 'my-input '+className;
return (
<Input className={computedClassName} {...rest} />
);
}
MyInput.defaultProps = Input.defaultProps;
MyInput.propTypes = Input.propTypes;
MyInput.Group = Input.Group;
MyInput.Search = Input.Search;
MyInput.TextArea = Input.TextArea;
Now if I put <MyInput /> inside of <Form/> it stops working
DEMO
I tried to debug, looks like the saveRef function in rc-form/lib/createBaseForm is receiving null as component argument, so this makes me feel is a ref problem, but I'm not sure how to fix it :S
Nevermind I found the answer...
As per Refs documentation
refs doesnt work on stateless components, change it to class and worked

Resources