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.
Related
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()
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.
I have one component, it contains two other components. First "NotifyMessage" component is rendered for the whole page. Second "NotifyMessage" component is rendered just only inside pop up. Both components subscribe to the redux store and get appropriate message and type (success or error) from there. Currently, if something happens - "NotifyMessage" component rendered in both places (popup and whole page). What is the best approach to separate render logic? I'd like to render only one component in one place.
create a flag State , say "compAlreadyShown" with boolean value. Use it to conditionally show hide in popup.
I've added another message to my redux store for "pop'ups" cases and pass it to my common "NotifyMessage" component as a children. For now I've two sources of truth for messages in my store instead of one. May be there is a better solution but it fix my problem.
I am new on react. I am working on react application with redux. I have a form (I am using redux-form) by which I can save data or edit data.
my problem is , In edit mode I populate data using componentWillReceiveProps. and populated perfectly, now when I try to clear any field on form its again fill.
componentWillReceiveProps(nextProps){
this.props.dispatch(initialize('NewProject', nextProps.project.project[0]));
}
I would be grateful for any help.
Is there a reason you're not dispatching this action somewhere else, like in componentDidMount? I can't say without seeing more code, but it's possible that whenever you edit your form, React again calls componentWillReceiveProps and overwrites whatever you did with the behavior you've given your component.
Per the React documentation:
Note that React may call this method even if the props have not changed, so make sure to compare the current and next values if you only want to handle changes. This may occur when the parent component causes your component to re-render.
It may be a good idea for you to move your dispatch to a more predictable event if possible, like componentDidMount. Typically, your render method should be capable of handling different cases if a component has multiple possible states. You could also create an edit and save version of your component, and render one or the other based on the props you receive. The best way to "populate" data, as you put it, is to define propTypes for your component, and then use your props to insert that data into elements in the render method.
So i'm fairly new to React and I can't wrap my head around a concept on how to re-render a main component based on another component.
Lets say we have a to-do application and a to-do item can have a state (new, running, closed). We are displaying all to-do items in a list and we can filter the items based on their state. We are using a bootstrap dropdownbutton like component to set the filter, which is a React component. Now when we change the filter we obviously want to refresh the to-do items.
My question is, does the logic of the selected state belong in Flux/Redux or does the filter component just say "refresh your items" to the main component?
When you use Redux in React application, follow one simple rule - all your components are stateless (means, no component initializes its state or calls .setState() anywhere).
The redux way of design based on state container, one big object that holds all application state. As React component, being connected to Redux store, Redux will pass the state (or portion of it) into that component, as this.props.state property.
That high-order component (or smart component), renders its children components (or dumb components) and handles events from them.
If child component requires change, it triggers corresponding handler (which typically passed as props). The only way to change the state is to dispatch an action. That action contains a type and a payload and based on that type, corresponding reducer is selected. The reducer then produces a new state, based on previous state and action payload.
If in the result of reducer call, state got changed, Redux will re-render high-order component, passing new state in properties. So, all child components will be updated correspondingly.
Check this example and trace how AddTodo component calls .handleClick() and then upper to .onAddClick() which dispatches an action.
If you are using redux, then on your dropdown you should have an onchange handler that dispatches an action every time the value is changed with the selected state (new, running, closed).
Redux reducer will handle this action by changing some state accordingly in the store for example: display = 'completed'. In addition to this, your todo list should also be stored in the store and it will likely be an array.
Your react component should receive a the todo array and display as props, and therefore everytime any prop (todo array or display) change, it will trigger a re-render.
In this case, your component should only display those todos that are complete (i.e. in the render you check if the state of each todo === this.props.display.
So to answer your question: redux keeps the state of the dropdown, which is passed to your main component, your main component then render only the todo's that matches the criteria.
So in a very minimal way, you could pass a function down to the select box, which calls setState on the top-level component. When that state changes, it will re-render its child components. This pattern doesn't scale well, and leads to the same hell React + Flux is trying to get us away from (state everywhere). I would say, using Flux (or even better, Redux), trigger an action that sets the filter in your store, and use derived data based on the filter state + your list of todo's in the todo list.