React not rendering component with values passed from a DataTable - javascript

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.

Related

react component return problem when props are null

export default function RenderPages({storage, setStorage, state, setState}){
let elRefs= useRef()
if(!storage) return
if(!state.currentFileId || !state.currentFolderId) return
const content = storage[state.currentFolderId][state.currentFileId].content
return (
<div className="writing">
<input ref={elRefs}/>
{content.map((page, index)=>
<div className='textarea'>
<textarea placeholder='write here' value={page} id={"page"+index} onChange={(e)=>onChange(e, index)} rows={rows} cols={cols}></textarea>
</div>)}
</div>
)
}
There are some props(state, storage) and they are sometimes null value. What I am doing now is checking the values of state and storage, returning blank early if those values are null. If I don't return in advance, the variable "content" get error because it needs state and storage value. Now this is the problem. I want to use "useRef", and if the component return early "elRefs" is assigned null value, so I can't get DOM element. What should I do?
I put your booleans into single function and seperated the renderable part of component. If bool is false, that component is simply not rendered. Of course you can put in there other component which shows error data or loading gif or something like that. Hope this works!
export default function RenderPages({ storage, setStorage, state, setState }) {
const elRefs = useRef()
const content = storage[state.currentFolderId][state.currentFileId].content
// content to render in seperate component
const Pages = () => (
<div className="writing">
<input ref={elRefs} />
{
content.map((page, index) =>
<div className='textarea'>
<textarea
placeholder='write here'
value={page} id={"page" + index}
onChange={(e) => onChange(e, index)}
rows={rows}
cols={cols}
/>
</div>
)
}
</div>
)
// decide to or not to render component
const renderable = storage &&
state.currentFileId &&
state.currentFolderId
return (
<>
{
renderable
? <Pages />
: <></> // returns empty component
}
</>
)
}

How can I return a React Component from a function and render it onClick?

I have an array of objects that I am looping through in my higher order component. On click I want to pass one of those objects to my component and render that component. The problem I am having is it's unclear how to return the component and have React update the render to show that component. I've read several Stackoverflow posts on this and the problem of putting the component in the html is it passes all the items in my loop to my component instead of just the one I need onClick.
The warning I'm seeing in my console is react_devtools_backend.js:3973 Warning: Functions are not valid as a React child. This may happen if you return a Component instead of <Component /> Which makes sense however, I'm not sure what the proper syntax is to solve this problem. Below is the relevant code.
const FormGroup = ({index}) => {
const renderSection = form => ( // I want to render this
<AdditiveSection
form={form}
register={register}
errors={errors}
/>
);
const addSection = form => {
setAdditionalSection(prevState => !prevState);
console.log('form', form);
renderSection(form);
};
return (
<section>
<FormProvider {...methods}>
<form onSubmit={methods.handleSubmit(onSubmit)}>
{myForm.controls.map(form => {
if (form.type === 'section') {
return (
<FormSection>
<div className="section__label">
<h4>{form.label}</h4>
</div>
...
{form.button
&& (
<FormAdd>
<LinkButton
type="button"
onClick={() => addSection(form)} // called here and passes my form object
>
<span className="button--small">{form.button}</span>
</LinkButton>
</FormAdd>
)}
{renderFormType(form)}
</FormSection>
);
}
})}
// renderSection should render here outside of the loop
{additionalSection && renderSection}
<input type="submit" />
</form>
</FormProvider>
</section>
);
Put it in state and just render.
const { extraSection, setExtraSection } = useState(null);
const addSection = form => {
setExtraSection(form);
};
return (
...
// renderSection should render here outside of the loop
{extraSection}
);
The problem with your original approach is that you were not saving that element anywhere. You called renderSection upon click, but that just called the function without storing or displaying that code anywhere.
Then in you render method, you again referenced rederSection. This is just a copy of the function, now without a parameter. It doesn't have any element as parameter, and it's not even called, so React is complaining that you're trying to render a function, instead of an element.
try it
const renderSection = form => (
return(
<AdditiveSection
form={form}
register={register}
errors={errors}
/>
)
);
Just in case anyone else may be struggling with this. Thanks to szaman and this article. Turns out I need to pass the section in the loop, but just pass through the data that was clicked.
const addSection = form => {
console.log('form', form);
setAdditionalSection(form);
};
return (
<section>
<FormProvider {...methods}>
<form onSubmit={methods.handleSubmit(onSubmit)}>
{myForm.controls.map(form => {
if (form.type === 'section') {
return (
<FormSection>
<div className="section__label">
<h4>{form.label}</h4>
</div>
...
{form.button
&& (
<FormAdd>
<LinkButton
type="button"
onClick={() => addSection(form)} // called here and passes my form object
>
<span className="button--small">{form.button}</span>
</LinkButton>
</FormAdd>
)}
{additionalSection && additionalSection.position === ind && (
<AdditiveSection
form={additionalSection}
register={register}
errors={errors}
/>
)}
{renderFormType(form)}
</FormSection>
);
}
})}
<input type="submit" />
</form>
</FormProvider>
</section>
);

How can I make a component render onClick in a React functional component?

I'm a bit surprised I'm having trouble finding this online, but I can't seem to find an example of how to do this in a React functional component. I have a React component that I would like to render when I click a button. Right now the function fires and I can see my console.log firing, however the component isn't rendering. My first guess was that it won't render because React doesn't know to update the view, however I added boolean via useState and it still won't render. What am I doing wrong?
Below is the relevant code. How can I get the component in addSection to render?
const FormGroup = ({index}) => {
const [additionalSection, setAdditionalSection] = useState(false);
const addSection = form => {
setAdditionalSection(true);
console.log('form', form);
return additionalSection && (
<div key={form.prop}>
<p>This should render</p>
<AdditiveSection
form={form}
register={register}
errors={errors}
/>
</div>
);
};
...
return (
...
<FormAdd>
<LinkButton
type="button"
onClick={() => addSection(form)}
>
span className="button--small">{form.button}</span>
</LinkButton>
</FormAdd>
);
You should change your state (or a prop in your useEffect dependency array in case you had one) in order to force a rerender. In this case:
setAdditionalSection(prevState=>!prevState);
A state change like the one you are calling, will trigger a re-render.
But all html to be rendered must be included in the functional components return statement.
The elements you want to render can be conditionally rendered like this:
const FormGroup = ({index}) => {
const [additionalSection, setAdditionalSection] = useState(false);
const addSection = form => {
setAdditionalSection(true);
console.log('form', form);
};
...
return (
...
<FormAdd>
<LinkButton
type="button"
onClick={() => addSection(form)}
>
<span className="button--small">{form.button}</span>
</LinkButton>
{additionalSection &&
<div key={form.prop}>
<p>This should render</p>
<AdditiveSection
form={form}
register={register}
errors={errors}
/>
</div>
}
</FormAdd>
);

Search input onchange causes crashing - how to make it a button to submit search

I have an app in MeteorJS, which makes use of React (I am ok with JavaScript, but am on a learning curve starting with React). The current search input makes use of the onchange function of the input box BUT this is actually not desired as this slows the app considerably - making requests every time the user types.
I basically want the input to be basic input and then have a button to trigger the search.
Inline code, for calling the searchinput where needed;
<div className="col-md-4 col-xs-12" style={disabledStyling.control}>
<SearchInput placeholder="Search" onChange={this.filterGames} value={filter} />
</div>
searchinput component;
import PropTypes from 'prop-types';
import Icon from '../Icon';
import Styles from './styles';
const SearchInput = ({ placeholder, value, onChange }) => (
<Styles.SearchInput className="SearchInput">
<Icon iconStyle="solid" icon="search" />
<input
type="text"
name="search"
className="form-control"
placeholder={placeholder}
value={value}
onChange={onChange}
/>
</Styles.SearchInput>
);
SearchInput.defaultProps = {
placeholder: 'Search...',
value: '',
};
SearchInput.propTypes = {
placeholder: PropTypes.string,
value: PropTypes.string,
onChange: PropTypes.func.isRequired,
};
export default SearchInput;
Hoping you all could help ;)
Basically what you need to do is use a state to store the value from the onChange event and later, on button click/form submit action you pass this value to the function that will actually fetch data.
Here is a small example on code sandbox where you can see this being applied both on a functional component and on a class component
lets assume your "wrapper" component is something like this:
const Form = () => {
const filterGames = (event) => {
// handle event and calls API
}
return (
<div className="col-md-4 col-xs-12" style={disabledStyling.control}>
<SearchInput placeholder="Search" onChange={filterGames} value={filter} />
</div>
)
}
What we need to do here, is basically adding the state and handle it without calling the API and a button to actually call the API.
const Form = () => {
const [inputValue, setInputValue] = useState('');
const filterGames = (event) => {
// handle event and calls API
}
// this will store the value locally on the state
const handleInputOnChange = (event) => {
setInputValue(event.target.value);
}
return (
<div className="col-md-4 col-xs-12" style={disabledStyling.control}>
<SearchInput placeholder="Search" onChange={handleInputOnChange} value={inputValue} />
<button type='submit' onClick={filterGames}>Submit</button>
</div>
)
}
ps: you can also wrap the input + button with a form and use form.onSubmit instead of button.onClick.

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.

Categories