Access the model from the Edit component - javascript

I have the following edit component
const UserEdit = (props) => (
<Edit {...props}>
<TabbedForm >
<FormTab label="User Account">
<DisabledInput source="id" />
<TextInput source="email" />
<TextInput source="password" type="password"/>
<TextInput source="name" />
<TextInput source="phone" />
<SelectInput source="role" optionValue="id" choices={choices} />
</FormTab>
<FormTab label="Customer Information">
<BooleanInput label="New user" source="Customer.is_new" />
<BooleanInput label="Grandfathered user" source="Customer.grandfathered" />
</FormTab>
</TabbedForm >
</Edit>
);
The second FormTab (Customer Information) I only need it to appear if the User model has some information associated (the JSON is something like):
{
id: <int>,
name: <string>,
....
Customer: {
is_new: true,
grandfathered: true,
}
}
I'd like to know if I can access somehow the model information (in this case, if the Customer key exists and has info) in order to be able to render or not the <FormTab label="Customer Information">
I'm a little bit lost with the global redux state. I know the data is in the state because I've debugged it with the Redux tools. (I tried to look in this.props but I couldn't find anything to access the global state)
Thanks.

If you need the object when you are building the form you could connect it to the redux store.
import { connect } from 'react-redux';
// this is just a form, note this is not being exported
const UserEditForm = (props) => {
console.log(props.data); // your object will be in props.data.<id>
let customFormTab;
const id = props.params.id;
if (typeof props.data === 'object' && && props.data.hasOwnProperty(id)) {
if (props.data[id].something) {
customFormTab = <FormTab>...;
}
}
return <Edit {...props}>
<Formtab...
{customFormTab}
</Edit>;
}
// this is your exported component
export const UserEdit = connect((state) => ({
data: state.admin.User.data
}))(UserEditForm);
PS: I'm not sure if this is a nice or desired solution (hopefully someone will correct me if I'm doing something not according to how redux or admin-on-rest was designed).

Related

SonarQube "Do not define components during render" with MUI/TS but can't send component as prop

I am getting the following error during sonarqube scan:
Do not define components during render. React will see a new component type on every render and destroy the entire subtree’s DOM nodes and state. Instead, move this component definition out of the parent component “SectionTab” and pass data as props. If you want to allow component creation in props, set allowAsProps option to true.
I understand that it says that I should send the component as a prop from the parent, but I don't want to send the icon everytime that I want to use this component, is there another way to get this fixed?
import Select from "#mui/material/Select";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { faAngleDown } from "#fortawesome/pro-solid-svg-icons/faAngleDown";
const AngleIcon = ({ props }: { props: any }) => {
return (
<FontAwesomeIcon
{...props}
sx={{ marginRight: "10px" }}
icon={faAngleDown}
size="xs"
/>
);
};
const SectionTab = () => {
return (
<Select
id="course_type"
readOnly={true}
IconComponent={(props) => <AngleIcon props={props} />}
variant="standard"
defaultValue="cr"
disableUnderline
/>
);
};
export default SectionTab;
What can you do:
Send the component as the prop:
IconComponent={AngleIcon}
If you need to pass anything to the component on the fly, you can wrap it with useCallback:
const SectionTab = () => {
const IconComponent = useCallback(props => <AngleIcon props={props} />, []);
return (
<Select
id="course_type"
readOnly={true}
IconComponent={IconComponent}
variant="standard"
defaultValue="cr"
disableUnderline
/>
);
};
This would generate a stable component, but it's pretty redundant unless you need to pass anything else, and not via the props. In that case, a new component would be generated every time that external value changes, which would make it unstable again. You can use refs to pass values without generating a new component, but the component's tree won't be re-rendered to reflect the change in the ref.
const SectionTab = () => {
const [value, setValue] = useState(0);
const IconComponent = useCallback(
props => <AngleIcon props={props} value={value} />
, []);
return (
<Select
id="course_type"
readOnly={true}
IconComponent={IconComponent}
variant="standard"
defaultValue="cr"
disableUnderline
/>
);
};

React not rendering component with values passed from a DataTable

I'm trying to get in practice with GraphQL in React, consuming nHost backend service to retrieve and save data from/to a database. I succesfully made into log in and log out in the app, but now turns out a problem. Showing the data in a DataTable, works just fine, but the problem is: when I try to render a component passing data as props, the component does not render at all. In theory: the button showed at the end of the code (rendered only if a selection of the table is made), should trigger the handleView, which should render the component with the props passed by.
Note: console.log shows data. Also, React router dom is implemented in the app. Does it matter? Or just my code is not properly implemented?
Code below:
const [searchInput, setSearchInput] = useState('');
const [selectedclient, setSelectedclient] = useState(null)
const handleView = (e) => {
e.preventDefault();
console.log(selectedclient)
if(selectedclient != null)
return <MainPanel data={selectedclient}/>
}
return (
<div>
<form onSubmit={(e) => handleView(e)}>
<input
className="input"
placeholder="insert the user you are looking for"
value={searchInput}
onChange={e => setSearchInput(e.value)}
></input>
<div className="card">
<DataTable value={filtered} selectionMode="single" selection={selectedclient} onSelectionChange={e => setSelectedclient(e.value)} dataKey="name" responsiveLayout="scroll">
<Column selectionMode="single" headerStyle={{width: '3em'}}></Column>
<Column field="name" header="Name"></Column>
<Column field="id" header="id"></Column>
</DataTable>
</div>
{filtered?.length != 0 && <button type="submit" >Select</button> }
</form>
</div>
)
The component to be rendered:
const MainPanel = (data) => {
const { name } = data
return (
<div>
<div>MainPanel</div>
<h3>{name}</h3>
</div>
)
}
Thanks in advance.

ref.current is null in gatsby react app when trying to execute recaptcha

I am trying to use this https://react-hook-form.com/get-started npm package with this package https://www.npmjs.com/package/react-google-recaptcha in gatsby and react. I want to use the invisible recaptcha it looks like I have to execute the recaptcha which I am trying to do by creating a react ref but it says the rec.current is null, Not quote sure what to do. The onSubmit function is where I am getting the null result, I was assuming I would be able to fire the captcha here and then get back the captcha value to later send off to google in my lambda function for verification.
Thanks ahead of time
Here is my code thus far
import React, { useState } from "react"
import Layout from "../components/layout"
import Img from "gatsby-image"
import { graphql, Link } from "gatsby"
import { CartItems } from "../components/cart"
import { useForm } from "react-hook-form"
import ReCAPTCHA from "react-google-recaptcha"
const StoreDetails = ({ data }) => {
const { register, handleSubmit, watch, errors } = useForm()
const recaptchaRef = React.createRef()
const onSubmit = data => {
console.log(recaptchaRef)
recaptchaRef.current.execute() //this shows up null
}
function onChange(value) {
console.log("Captcha value:", value)
}
function error(value) {
alert(value)
}
return (
<>
{data.allSanityProducts.edges.map(({ node: product }, i) => {
return (
<React.Fragment key={i}>
<Item>
<Img
fluid={product.featureImage && product.featureImage.asset.fluid}
/>
<div>
...
<form onSubmit={handleSubmit(onSubmit)}>
{/* register your input into the hook by invoking the "register" function */}
<input name="example" defaultValue="test" ref={register} />
{/* include validation with required or other standard HTML validation rules */}
<input
name="exampleRequired"
ref={register({ required: true })}
/>
{/* errors will return when field validation fails */}
{errors.exampleRequired && (
<span>This field is required</span>
)}
<ReCAPTCHA
className="captchaStyle"
sitekey="obsf"
onChange={onChange}
onErrored={error}
badge={"bottomright"}
size={"invisible"}
ref={recaptchaRef}
/>
<input type="submit" />
</form>
</div>
</Item>
</React.Fragment>
)
})}
{close && <CartItems />}
</>
)
}
const WithLayout = Component => {
return props => (
<>
<Layout>
<Component {...props} />
</Layout>
...
</>
)
}
export default WithLayout(StoreDetails)
export const query = graphql`
query StoreDeatailsQuery($slug: String!) {
...
}
`
You are never populating the reference with any value. Initially is set to null in:
const recaptchaRef = React.createRef()
You have to wait for the Google response to fill the recaptchaRef with a value. In other words, you need to use a promise-based approach to fill it using an executeAsync() and using an async function:
const onSubmit = async (data) => {
const yourValue = await recaptchaRef.current.executeAsync();
console.log(yourValue)
}
You can check for further details about the props exposed in react-google-recaptcha documentation.

How can I accessing nested component in react from parent component?

I want to access a nested component from parent component.
This is Bill Form.jsx
import BillDetailForm from './BillDetailForm';
render(){
return (
<form onSubmit={handleSubmit}>
<FieldArray
name= 'detail'
component={BillDetailForm}
placeholder= '...detail'
label='Detail'
/>
</form>
);
}
}
BillForm is the parent component.
This is a nested component or child component of BillForm: BillDetailForm.jsx
render(){
return(
<form onSubmit={ handleSubmit }>
<div>Detail:</div>
<FieldArray
name= 'detail'
component={RenderDetail}
label='Detail'
/>
</form>
)
}
Inside BillDetailForm is RenderDetail:
const RenderDetail = ({fields, meta: { error,submitFailed}},props) => (
<dl>
<dt>
<button type="button" className= 'btn btn-primary' onClick={() => fields.push()}>Add
Detail</button>
{submitFailed && error && <span>{error}</span>}
</dt>
{ fields.map((registerDetail, index) =>
//In the following line renderDetail is accesing Detail component.
<Detail detailItem={registerDetail} fields={fields} index={index} key={index}/>
)
}
{error && <dt className="error">{error}</dt>}
</dl>
);
This is Detail Class Component:
class Detail extends Component{
render(){
const{detailItem,index,fields,isSubtotal} = this.props;
return(
<dd key={index}>
<br></br>
<button className= 'btn btn-light mr-2'
type="button"
title="Remove detail"
onClick={() => { fields.remove(index)
if(fields.length == 0 || fields.length === undefined){
}
try{
for(let x in fields){
fields.remove(index)
let d = fields.selectedIndex;
if(fields.remove(index) && d >= 1 && d< fields.length ){
fields.removeAll(index);
}
}
}catch{console.info("deletes non numerical index")}
}}> Delete </button>
<h4>DetailRegister #{index + 1}</h4>
<Field
id={`${detailItem}._id`}
name={`${detailItem}.quantity`}
component= {NumberPickerInteger}
placeholder= '...quantity'
label = "Quantity"
/>
<br></br>
<h3><b>Product</b></h3>
<Field
id={`${detailItem}._id`}
name={`${detailItem}.product.code`}
type="number"
component= {RenderFieldNumeric}
placeholder='...Product's code'
label = "Product's code"
/>
<Field
id={`${detailItem}._id`}
name={`${detailItem}.product.name`}
type="text"
component= {RenderField}
placeholder='...Product's name'
label = "Product's name"
/>
<Field
id={`${detailItem}._id`}
name={`${detailItem}.product.price`}
component= {NumberPickerr}
placeholder= '...Price'
label = "Product's price"
/>
<br></br>
<h3><b>Subtotal</b></h3>
<Field
id={`${detailItem}._id`}
name={`${detailItem}.subtotal`}
component= {SubtotalWidget}
placeholder= '...subtotal'
label = "Subtotal"
>
{isSubtotal}
</Field>
</dd>
);
}
}
I want to access e.g ${props.detailItem}.subtotal that is in Detail from BillForm. BillForm accesses to BillDetailForm, BillDetailForm accesses to renderDetail, and last renderDetail acceses to Detail.
The question is: How can I access and use props like quantity and subtotal with dynamic index (props.index) from BillForm? I want to access Detail component from BillForm, respecting the following secuence in order access: BillForm -> BillDetailForm -> RenderDetail -> Detail
If I understand correctly what you are saying, it seems you are going against the ethos of React. If your parent component wants access to a piece of data, then that data should start in the parent and be passed down. This way, if the data changes it will call a re-render of components and update all necessary components.
Some other advice. Try not o have so much logic inside your component handlers, it looks messy and will run every render cycle. Abstract this into a method on the class and call it when required.
My example will hopefully help you with your issue, but I recommend having a read of the React documentation as it is very good with simple examples.
The use of class will be deprecated eventually in favour of function components and the Hooks API.
class ParentComponent {
state = {
value: 0,
}
methodToDoSomething = (passedVal) => {
this.setState({
value: passVal,
});
}
render() {
const myState = this.state;
return (
<Component {...myState} />
)
}
}
class Component {
state = {}
render() {
const { value , methodToDoSomething } = this.props;
return (
<div onClick={methodToDoSomething}>
{value}
</div>
)
}
}
// Hooks API
const ParentComponent = () => {
const [stateVal, updateState] = React.useState('myString');
return (
<div>
{stateVal}
<Component passedVal={stateVal} passedHandler={updateState} />
</div>
)
}
const Component = ({ stateVal, passedHandler }) => {
function updateMyValue() {
passedHandler('menewvalue');
}
return (
<div onClick={updateMyValue}>
{stateValue}
<div/>
)
}
To avoid passing lots down all the children components, I would recommend reading up on the Context Hook.
*** UPDATE ***
The above example is rudimentary and tries to answer the question presented, there are always many ways to solve a problem.
Passing props can be messy and a maintenance overhead. Most larger applications will benefit from using a state library to manage their global state. The Context API is a good tool to use to wrap a cohesive set of components so they can share data/props without prop-drilling (passing props down many child components).
Custom hooks are another good way to share data. Create a hook containing your data and any other methods for the task and use this hook inside parent and child components to share said data.

React, Keep getting Cannot read property 'value' of undefined on Search Input

Im trying to integrate the < ChipInput /> component from https://github.com/TeamWertarbyte/material-ui-chip-input I'm using material-UI react component and so far what I have is:
A search input bar. When I type an artist it returns a result. so basically is working.
Now, when I try to implement < ChipInput /> , following the instructions I get no results. (note IconButton and TextField are commented out as in Im trying to replace them with ChipInput)
Therefore, if I type "aerosmith" I'll get:
FETCH_URL https://itunes.apple.com/search?term=&limit=4 instead of
FETCH_URL https://itunes.apple.com/search?term=aerosmith&limit=4
so , it's like is not taking my setState query for a reason. I tried componentWillReceiveProps but it didnt help. Any suggestions ?
class Searcher extends React.Component {
constructor(props){
super(props);
this.state = {
query: [],
application: null,
}
}
componentDidMount () {
this.handleSearchRequest();
}
handleSearchRequest() {
console.log('this.state', this.state);
// we will replace BASE_URL with Anu's search api
const BASE_URL = 'https://itunes.apple.com/search?';
const FETCH_URL = BASE_URL + 'term=' + this.state.query + '&limit=4';
console.log('FETCH_URL', FETCH_URL);
fetch(FETCH_URL, {
method: 'GET'
})
// Initial test to see what the console response is after hiting the API
// .then(response => console.log('response', response));
.then(response => response.json())
// const applications = response.data
// this.setState({applications})
//testing the json results
.then(json => {
// console.log('json', json)
const application = json.results[0];
this.setState({application})
console.log({application})
});
}
handleChange(event) {
this.setState({ query: event.target.value})
}
render () {
return (
<div style={{position: 'relative'}}>
{/* <IconButton
iconStyle={styles.smallIcon}
style={styles.iconButton}
onClick={() => this.handleSearchRequest()}
>
<Search color={black} />
</IconButton>
<TextField
underlineShow={false}
id="searchId"
value={this.state.query}
fullWidth={true}
style={styles.textField}
inputStyle={styles.inputStyle}
hintStyle={styles.hintStyle}
onChange={event => {this.setState({ query: event.target.value}) }}
onKeyPress={event => {
if (event.key === 'Enter') {
this.handleSearchRequest()
}
}}
/> */}
<br/>
<br/>
<ChipInput
fullWidth={true}
defaultValue={this.state.query}
onChange={(event) => this.handleChange(event)}
/>
{
this.state.application != null
?
<ResultItem
{...this.props} {...this.state}
application={this.state.application}/>
: <div></div>
}
</div>
);
}
}
export default Searcher;
EDIT:
By the way, if I uncomment from < IconButton /> (line 110) til the end of < TextField /> (line 133) it does exactly what I want , but no chips ( with no ChipInput of course)
You dont need a defaultValue all you need is 4 things (if you want autocomplete) searchText, dataSource, onUpdateInput and Onchange.
Material UI autocomplete and chips properties are similar so apply them to ChipInput, they are almost the same.
Bottom line is you have to write a function for every property you use within ChipInput expect for searchText, which can actually be a string. As you can see, ChipInput might be easy with hardcoded values, but when you start hitting APIS it is not so easy anymore. it is important to realize what onUpdateInput does
Also, you are supposed to bind every function you write within the constructor this is a react pattern that ensures performance, found it on a book.
constructor(props) {
super (props)
this.onUpdateInput = this.onUpdateInput.bind(this);
this.onNewRequest = this.onNewRequest.bind(this);
}
Then on the render method
<ChipInput
searchText={this.state.query}
fullWidth={true}
dataSource={this.state.dataSource}
onUpdateInput={this.onUpdateInput}
onChange={this.onNewRequest}
/>

Categories