I have a button on my parent component and a third-party form in a child component. When the user clicks the button, the save function in the child component should run. This is my plan.
Method 1 I tried:
1.Created a variable on parent called save.
2. When button is clicked, save becomes true
3. Save is passed down to the child as props
4. Child contains a useEffect() which listens to changes in props.save
5. If props.save is true, save function of child component runs
Method 2 I tried:
Instead of passing props, I created a react-redux store.
The store contains save variable which is false by default
When button is clicked, save in redux becomes true
I use useSelector() hook to listen to the save variable change, inside the child component
UseEffect is used to run the save() function when the value change is detected
What happens with both methods is that I am losing the data in my child component because the variable change in the parent causes a page refresh. Therefore I want to pass the data down to the child, without causing rerenders. What are the options I have?
Thanks in advance
Thanks to #Shyam, I could finally solve this issue!
My assumption that useState and props cause render was correct. And as #Shyam suggested, there is no direct way to avoid it.
I am using react-editor-js in my project and that's what caused the issue
<EditorJs
instanceRef={()=>{//code to save the reference as state variable}}
enableReInitialize
data={{}}
tools={EDITOR_JS_TOOLS}
/>
The reason for state loss was that my instanceRef was being reassigned every time the component renders.
This reassignment can be prevented by wrapping the method to save the reference as a state variable with useCallback()
Related
I am running some tests with react to find out a way for a parent to send an "event" to a child component. In the test application, the parent (CounterController) has a button. When the button is clicked, the parent shall send a reset "event" to the child (Counter) to reset the current count. Like below.
I am new to react, and can not find a way to dispatch/receive events or messages between components. I had a thought to mark the "reset" request as a "state" in the parent, then pass it to the child through props. Then the child clear the "reset" state, through a callback function, to avoid repeating "reset" requests. However, I got the code run into an infinite loop. Here are my codes.
The parent component - CounterController
The child component - Counter
the error logs
I do not feel that I am using react the right way, by asking the child to do something from the parent. Anyway, any idea about how to implement this app in react? How should the data flow be built correctly? Thanks!
You can move up the counter state on the parent ‘CounterController’
and then you pass the counter and setCounter as props to ‘Counter’
You don’t need ‘reset’ state
I am posting my thoughts about using react (as a beginner), and my solution to the app.
treat the react components as the "view" of the application, and maintain the data and controls in the hooks.
create a custom hook to wrap up all data and controls.
create an instance of the custom hook in the top component - display the data in HTML elements and bound controls to buttons to build the app UI
Here are my codes for this simple UI
UI
useCounter hook
counterController component
counter component
Move "count" state from child component (Counter) to parent
component (CounterController).
Pass count state as prop to child component.
Pass increment and decrement methods from parent to child component through props.
Note: Remove reset state, it's not required anymore.
Sandbox link : https://codesandbox.io/s/infallible-platform-vo4qtv?file=/src/CountController.js
I am learning react and I have been struck in understanding the flow of useState and useEffect hooks(how do they re-render the component) and what exactly does re rendering a component mean(Will the whole function be run again or how does it happen).I am attaching a demo code below it would be great if you explain how the hooks work in this case.
const [myNumber, setMyNumber] = useState(0);
let inc2=1;
function increment() {
setMyNumber(myNumber + 1);
inc2++;
console.log(inc2);
}
return (
<div>
<p>{inc2}</p>
<p>{myNumber}</p>
<button onClick={increment}>Increment!</button>
<p>{inc2}</p>
</div>
);
}
Here once I click the button setMyNumber triggers a re-render right?So
Will it trigger re render in the middle of the function execution?
2)If it re renders at the end shouldn't the inc2 value be updated on the screen because when I print it on console it is updated.
The state of the component helps us to indicate to react how the component should be displayed.
React has a "copy" of your dom called virtual dom and when the state changes, using the function returned by useState, react re-renders the componen wich means that the whole function of the component executes again and then react compares the new dom with the virtual dom, if there is some change then react updates the screen.
In your component the inc2 variable is declared again in every render and is initialized with 1, but myNumber is just initialized with 0 only once and won't be initialized again in the future renders, to change its value you must use the setMyNumber func. You can do something like:
myNumber = 1
But react does not know that the state changed and won't refresh the page, that's why you should use the setMyNumber to let to react know that has to re-valuate the component.
The useEffect hook is useful to handle side effects, to understand this hook i recommend you this article
https://medium.com/#dev_abhi/useeffect-what-when-and-how-95045bcf0f32
Will it trigger re render in the middle of the function execution?
When React can tell that the function running is from inside React - such as a JSX click handler - it will optimize performance by waiting to re-render until the handler (and other work) is finished. This means that if multiple state setters are called, they can all be consolidated into a single re-render at the end, rather than one re-render for each state setter call.
If React can't tell that the function is inside React - such as directly inside a setTimeout - then it will re-render immediately, because React doesn't know when whatever processing is going on will finish. But -
2)If it re renders at the end shouldn't the inc2 value be updated on the screen because when I print it on console it is updated.
No matter when the re-render occurs, your increment function has scope of the myNumber as it was at that time the component was rendered. Re-rendering the component doesn't call increment again. The increment function has a closure over the state value at the same time the handler was defined. So if myNumber contains some value at the time the handler is defined, and that handler runs, that handler will continue to see myNumber as containing that value throughout its execution, no matter whether or when a state setter is called inside.
Why the component Demo does not rerender on counter change?
I understand that props.children are equal to the previous ones, however local state changes, so it should rerender. Is changing local state optimized somehow to detect whether some part of the Top component should be rerendered or shouldn`t?
Children props example
In the second example it does rerender, what is the difference between these examples?
Local state change
In the examples you can click on button and see in the console whether Demo is rerendered.
In the first case you're passing the children which is a part of props and essentially on each re-render of Top component, the same children reference is being returned so React detects that and doesn't re-render the Demo component.
In the second case, React will internally execute React.createElement(...) again to create the Demo component and that's a new reference.
Here is a good line to remember this by :-
if a React component returns the exact same element reference in its
render output as it did the last time, React will skip re-rendering
that particular child
I referred this here - https://blog.isquaredsoftware.com/2020/05/blogged-answers-a-mostly-complete-guide-to-react-rendering-behavior/#component-render-optimization-techniques
This is because passing children argument to the Top component you are passing a reference to this object. React can detect that nothing has changed comparing to the previous version so it will not re-render it.
When you are rendering component using name <Demo /> in your Top component you are rendering a new version of Demo component every time.
Will componentDidUpdate always fire for a component that renders empty fragment/null ? It receives redux connected props from the parent container.
This component needs to access previous props and compare, then make an API call based on it. It doesn't have UI, so it renders empty fragment (or null)
EDIT : It is working and firing componentDidUpdate in my sample application. Is this guaranteed to be the behavior always?
It says here that there is a chance it may not fire when there was no change in the virtual DOM: Props updated, componentDidUpdate did not fire
MyContainer
|
-MyComponent
Yes it run every time instead at time of first initial render
Yes componentDidUpdate is called every time, even when rendering null. You can check this in the react documentation: React Lifecycle
I am trying to add multiple Slate.js text editor fields into one page. So far my main component contains a button which when clicked appends a slate value to an array slateValues in my redux store. In the render function I then map over that array and for each entry return a SlateEditor component which essentially renders the standard Slate Editor component with some custom formatting/functionality.
My problem is that Slate uses an onChange function to process changes to the value. Handling those changes in the local state works fine, but how can I pass that into the redux store? Updating the redux store directly in the onChange causes the parent component to re-render which then ends up in an endless loop (I assume this then triggers the onChange again, which triggers a re-render etc.).
I initially passed down the values as props into the SlateEditor component, then tried to directly read the value in the child component (SlateEditor) from the redux store.
My final aim is to store the slateValues as a "block" in a database. Any ideas on how to fix this? Thanks
I had similar issue.
What I did was I did not map over the array of slate values.
Instead I had another array of (my case) editorsNamespaces=[....] also I pass to the Slate editor wrapper component a selector (ie: selectSlateValueByEditorByNamespace) and a onChangeHandler(newValue, nameSpace).
By doing that my parent component will not re-render all editor because editorsNamespaces array will never change, and only particular editor will re-render when value has changed.