I have a rather big array of objects in the parent component's state. And when I pass them as props to children components (which don't have state, only props) I have a huge delay in typing (in children's components) when the input changes.
I have already read answers on similar questions ReactJS delay onChange while typing and advice to use shouldcomponentupdate reactjs : ShouldComponentUpdate for states .
But, unfortunately, I still didn't understand how to apply it in my example: https://codesandbox.io/s/react-example-8vlc2 . Parent component is index.js . So:
1) Should I use componentdidupdate or shouldcomponentupdate() in children's components (StoryList.tsx and StoriesScreenItem/StoriesScreenList) ?
2) Should I add state in children's component to use componentdidupdate or shouldcomponentupdate() ?
3) Why does the input delay happen in my example?
4) Or any other ideas how can I manage this problem?
Any help would be appreciated!
My suggestion would be to create a functional component for a story.
It could look something like this:
export const Story = props => {
const [story, setStory] = useState(props.story);
const handleChange = e => {
const updatedStory = {...story};
updatedStory.caption = e.target.value;
setStory(updatedStory);
};
return (
<div>
<div>
<div>
{story.previewUri ? (
<div>
<img
style={{
objectFit: "cover",
border: "4px solid #1ec1b6"
}}
width="110"
height="168"
src={story.previewUri || null}
alt={"Фото сториc"}
/>
</div>
) : (
<div
style={{
border: "4px solid #1ec1b6",
textAlign: "center",
fontSize: "14px"
}}
>
Link image
</div>
)}
<div>
<span>Story</span>
<div>
<div>
<span>Preview title</span>
</div>
<div>
<input
style={{ width: "100%" }}
maxLength={99}
value={story.caption}
onChange={e => handleChange(e)}
/>
</div>
</div>
</div>
</div>
<StoriesScreenList
screens={story.stories}
onChange={screens => props.changeScreen(screens, props.index)}
/>
</div>
</div>
);
};
export default Story;
Each of these would have their own state, so you would not have to update the entire array of stories each time a single story is changed.
Which is suspect is what creates the delay.
I Hope this is helpful.
Related
This problem has consumed a lot of time trying to figure out what is wrong. I have a React-Hook-Form (v6.14.1) that needs to populate dynamic data, based on the component state.
On the initial load, everything works fine. If I change the state all updated data are displaying fine, except the dynamic data.
Here is a codesandbox link. If it does not render due to a library error, just hit the preview refresh button.
The goal is that the WAN 1 tab, on initial load displays the dynamic fields (WAN 1 VLAN-1) and WAN2 does not have dynamic fields to display. Hitting the Update Config button, WAN1 should not have dynamic fields to display and WAN2 should display one (WAN 2 VLAN-1). The problem is that WAN2 does not display it.
I have searched for similar questions, but all of them were about the values of the populated fields and not about displaying the fields themselves. I have used the reset method of react-hook-form and the defaltValue for each dynamic field as react-hook-form documentation suggests.
On App.js I have the state, a button that updates the state, and the Form component which has the state as property.
const [configdata, setConfigdata] = useState(config);
return (
<div className="App">
<UpdateConfig onClick={() => setConfigdata(configUpdated)} />
<Form
formData={configdata}
handleFormData={(data) => console.log(data)}
/>
</div>
);
}
On Form.js there is a Rect-hook-form FormProvider and the WanFields component that dynamically populates form fields.
<FormProvider {...methods}>
<form
onSubmit={methods.handleSubmit((data) =>
props.handleFormData(data)
)}
>
<Tab.Content>
{props.formData?.intfs?.length &&
props.formData?.intfs.map((intf, index) => (
<Tab.Pane key={index} eventKey={`wan${index}-tab`}>
<WanFields
key={`wan${index}-fields`}
intfNo={index}
portTypeOptions={props.portTypeOptions}
data={intf}
/>
</Tab.Pane>
))}
</Tab.Content>
</form>
</FormProvider>
Every time the props.formData update, there is a useEffect that reset the forms' default data.
const methods = useForm({ defaultValues: props.formData });
useEffect(() => {
methods.reset(props.formData);
}, [props.formData]);
In WanFields.js, there are all the form fields, and the useFieldArray method, that will populate the dynamic fields based on the forms' default values and a watch field value (watchIntfType ).
const methods = useFormContext();
const { errors, control, watch, register } = methods;
const { fields, append, remove } = useFieldArray({
control,
keyName: "fieldid",
name: `intfs[${intfNo}].subIntfs`
});
const watchIntfStatus = watch(`intfs[${intfNo}].enabledStatus`);
const watchIntfType = watch(`intfs[${intfNo}].enabled`);
Dynamic fields are populated as follows
{watchIntfType?.value >= "2" && (
<>
<div className="form-group">
<div className="btn btn-success" onClick={append}>
Add
</div>
</div>
<div id={`accordion-${intfNo}`}>
<Accordion>
{console.log("FIELDS", fields)}
// This is where the problem starts. fields are empty after updating data
{fields.map((field, index) => {
return (
<Card key={field.fieldid}>
<Accordion.Toggle
as={Card.Header}
variant="link"
eventKey={`${index}`}
style={{ cursor: "pointer" }}
>
<h4>
WAN {parseInt(intfNo) + 1}{" "}
<span style={{ margin: "0px 5px" }}>
<i className="fas fa-angle-right"></i>
</span>{" "}
VLAN-{index + 1}
</h4>
<div className="card-header-action">
<button
type="button"
className="btn btn-danger"
onClick={() => remove(index)}
>
Remove
</button>
</div>
</Accordion.Toggle>
<Accordion.Collapse eventKey={`${index}`}>
<Card.Body>
<div className="form-row">
<div className="form-group col-12 col-md-6">
<label>IP</label>
<input
type="text"
className="form-control"
name={`intfs[${intfNo}].subIntfs[${index}].ipAddress`}
defaultValue={field?.ipAddress}
ref={register()}
/>
</div>
<div className="form-group col-12 col-md-6">
<label>Subnet</label>
<input
type="number"
className="form-control"
min="0"
max="30"
name={`intfs[${intfNo}].subIntfs[${index}].subnet`}
defaultValue={field?.subnet}
ref={register()}
/>
</div>
</div>
</Card.Body>
</Accordion.Collapse>
</Card>
);
})}
</Accordion>
</div>
</>
)}
The problem is that when the state updates, form default values are updated, but the method useFieldArray attribute fields are not updated and stay as an empty array. I really cannot understand, what I am doing wrong. Any help will be much appreciated.
I don't know if is a correct method but i have resolv this probleme with method reset in a useEffect.
https://react-hook-form.com/api/useform/reset
defaultValues:
{
acvDesignOffice: generateRSEnv.acvDesignOffice,
earthQuakeZone: generateRSEnv.earthQuakeZone,
buildings: generateRSEnv.buildings,
},
useEffect(() => {
reset({
acvDesignOffice: generateRSEnv.acvDesignOffice,
earthQuakeZone: generateRSEnv.earthQuakeZone,
buildings: generateRSEnv.buildings,
});
}, [generateRSEnv]);
I would like to scroll to menu element in a page.
I have the menu component which is not a parent of components to which I would like to scroll.
I have found this post that describe a similar problem
Passing ref to a child We want the ref to be attached to a dom element, not to a react component. So when passing it to a child
component we can't name the prop ref.
const myRef = useRef(null)
return <ChildComp refProp={myRef}></ChildComp> } ```
Then attach the ref prop to a dom element. ```jsx const ChildComp =
(props) => {
return <div ref={props.refProp} /> } ```
Here's my app structure
Menu component:
const MenuApp = () => {
return (
<div>
<div className="desktop-menu">
<div className="menu-item a-propos">
<p className='button'>Me découvrir</p>
</div>
<div className="menu-item competences">
<p className='button'>Compétences </p>
</div>
<div className="menu-item experiences">
<p className='button'>Experiences</p>
</div>
<div className="menu-item formation">
<p className='button'>Formation </p>
</div>
</div>
</div>
)
}
The parent is app component
<div className="App">
<div className="hero">
<HeaderApp />
<ApprochApp />
</div>
<Apropos />
<Competences />
<Experiences />
<Formation />
<Recom />
<Contact />
<Footer />
</div >
I would like that mu menus scrolls to the react components in the main App component
So how can I passe the reference from the menu component to the app and use it in components to scroll ?
I do not understand your problem completely though. However, one thing I can see from your question is that you're not forwarding the ref properly.
What you need in this case is forwardRef.
Basically, what you need to do is to create the childComponent as something like this:
const childComponent = React.forwardRef(({...otherProps}, ref) => {
return (<><div ref={ref}>Component content </div></>)
})
Where you need to use the component all you need to do is this:
const parentComponent = () => {
const reveiwsRef = React.useRef("");
return (
<div>
<childComponent ref={reviewsRef} />
</div>
);
}
You can find more info about this on the react documentation: Forwarding-Refs
I have hope this helps though
I have checked for 2 hooks violations, I have the same version of react and react-dom, and have no duplicate copy of react. I am unable to understand what the error is actually?
index.js:3221 Uncaught Invariant Violation: Invalid hook call. Hooks can only be called inside of the body of a function component.
This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
I have a set of filters that i want to show to user :
import Chip from "#material-ui/core/Chip";
import React, { Fragment } from "react";
import Text from "#material-ui/core/text";
function RenderFilter(props) {
return (
<Fragment>
<RenderHeader />
<RenderX {...props} />
<RenderSomething {...props} />
</Fragment>
);
function RenderSomething(props) {
const { filters } = props;
const allowedKeys = Object.keys(filters);
return (
<div>
{allowedKeys.map((item, index) => (
<Fragment key={index}>
<div
style={{
display: "flex",
justifyContent: "space-between",
margin: "8px 0px"
}}
>
<div style={{ borderBottom: "1px #f6f6f6" }}>
<div style={{ maxWidth: 200 }}>
<Text>{item}</Text>
</div>
<div>
{filters[item].map(ele => (
<Chip key={`${ele}`} color="primary" label={`${ele}`} />
))}
</div>
</div>
</div>
</Fragment>
))}
</div>
);
}
I have a webpage with several components that looks like:
<div id='someId' ...... />
Manually it is possible to go to those sections using
<a href="#someId" ..... />
How, using es2015 or react or javascript, could be created a function to go to that <div> component ?
You could use the scrollIntoView function.
document.getElementById('someId').scrollIntoView();
React Example (CodeSandbox)
class App extends React.Component {
onClick = () => {
this.ref.scrollIntoView();
};
render() {
return (
<div>
<button onClick={this.onClick}>Scroll down</button>
<div style={{ height: 1000 }}>Hello CodeSandbox</div>
<div style={{ height: 1000 }} ref={ref => (this.ref = ref)}>
Scroll here
</div>
</div>
);
}
}
If you don't mind installing a 3rd party package, I would suggest trying react-scroll-into-view
I was read some tutorial about this. They told me should using ref to do that.
But It's very general.
Here is my problem:
Basically in Header component include NavBar, SearchBar and ResultSearch component.
const Header = () => {
return (
<header className="ss_header">
<Navbar />
<SearchBar />
<ResultSearch />
</header>
);
};
And In SearchBar component. Whenever I focused on input text. It emit an event and display ResultSearch component by any way (changing style, or ...).
class SearchBar extends Component{
render() {
return (
<div className="search_bar">
<section className="search">
<div className="sub_media container">
<form method="GET" action="" id="search_form">
<Icon icon="search" />
<span className="autocomplete">
<input
className="search_input"
autoCorrect="off"
autoComplete="off"
name="query"
type="text"
placeholder="Search for a movie, tv show, person..." />
</span>
</form>
</div>
</section>
</div>
);
}
}
Style in ResultSearch component. I was set display: none.
.results_search { display: none; }
I think ResultSearch will receive an event from SearchBar and set back display: block for ResultSearch component. Is possible?
How can I handle that?
My Code here: https://codesandbox.io/s/3xv8xnx3z5
only you should convert Header component like following:
class Header extends Component {
state = {
focus: false
};
handleInputFocus = () => {
this.setState({ focus: true });
};
handleInputBlur = () => {
this.setState({ focus: false });
};
render() {
return (
<header className="ss_header">
<SearchBar
onFocus={this.handleInputFocus}
onBlur={this.handleInputBlur}
/>
{this.state.focus ? <ResultSearch /> : null}
</header>
);
}
}
and also in SearchBar component add following attributes to your input:
onFocus={this.props.onFocus}
onBlur={this.props.onBlur}
also, you should remove your CSS about result box.
And, you can see the updated code on the following sandbox:
https://codesandbox.io/s/mmj46xkpo9
Still not sure what you're trying to achieve.
This is the way you can handle visibility of result of the search. Let me know if this isn't what you're looking for.
https://codesandbox.io/s/7jvz31xr66