I am making a resume builder application and the whole structure was almost done.
Complete working codesandbox:
Here I have made components into stepper for each section,
index.js
<form onSubmit={handleSubmit}>
<Stepper
steps={sections}
activeStep={currentPage}
activeColor="red"
defaultBarColor="red"
completeColor="green"
completeBarColor="green"
/>
{currentPage === 1 && (
<>
<BasicDetails />
<button onClick={next}>Next</button>
</>
)}
{currentPage === 2 && (
<>
<EmploymentDetails />
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<button onClick={prev}>Back</button>
<button onClick={next}>Next</button>
</div>
</>
)}
{currentPage === 3 && (
<>
<pre>{JSON.stringify(value, null, 2)}</pre>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<button onClick={prev}>Back</button>
<button onClick={handleSubmit}>Submit</button>
</div>
</>
)}
</form>
Steps to reproduce issue:
-> In Step 1 Enter First Name, Last Name, and Profile Summary
-> Click on Next button to move to next step.
-> Then click the back button to move backward to Step 1 (Currently in Step 2)
-> Here the values in First Name, Last Name are there but the value entered in text editor alone gets lost
Please refer the below image with text editor value entered for better understanding of the problem I am facing..
Text editor value alone gets lost if we switch forth/back the steps. But the entered value is stored in the form Context and not rendered in the Text Editor box.
Note:
Text editor is made as a component and it was used in Step 1 (For profile summary) and also in Step 2 (For employment description) and in both cases switching between steps, the value entered inside the text editor gets lost.
Analysis:
Based on my understanding, this happens because on navigating to other steps, the component get re-rendered and the EditorContainer component gets called and in text_editor.js it was given
this.state = {
editorState: EditorState.createEmpty(),
};
So it was created as empty.
So how can I control the component from getting re-rendered so that the data entered in text editor won't get lost.
Kindly please help me to retain the values entered inside the text editor. Big thanks in advance..
This happens because we're only saving our EditorContainer value to our Context, but we're not using it when we rerender the EditorContainer component.
The fix would be to pass the saved value as prop to our EditorContainer component.
Then before we render the EditorContainer, we'll convert that value to EditorState which can be done using convertFromHTML function, and set that as our editorState state.
Step 1: Pass value prop to EditorContainer
// basic_details.js
<EditorContainer
name="profileSummary"
value={basicDetails.profileSummary}
onChange={(event) => handleInputChange(event)}
/>
// employment_details.js
<EditorContainer
name="description"
value={inputField.description}
onChange={(event) => handleInputChange(index, event)}
/>
Step 2: Convert the value prop to EditorState
// text_editor.js
...
componentDidMount() {
// https://draftjs.org/docs/api-reference-data-conversion/#convertfromhtml
const { value } = this.props;
const blocksFromHTML = convertFromHTML(value);
const state = ContentState.createFromBlockArray(
blocksFromHTML.contentBlocks,
blocksFromHTML.entityMap,
);
const editorState = EditorState.createWithContent(state);
this.setState({ editorState });
}
That's it! Check the demo below.
Edit Fix demo to check value if is string.
This is a great question. This is basically a design approach issue: For a scenario like yours, you need to design your component this way:
let's analyze the picture:
MAIN COMPONENT:
This is the component that should hold the state for the entire form-filling process. STE1-4 Are just views that allows you to input data that must all be updated in the main component. So This means, you must have state in main component and pass the state properties and props, including their update/setter methods.
STEP COMPONENT
This applies for all Step Components.
These components should do nothing except display the form step using state values received via props and update state by using setter methods, which are also received via props.
Conclusion:
Put your state in your main component, each step component should only display the form and update the main state. This means that by the time each step component is re-rendered, it will receive values updated in the main component. And you will achieve that via props.
It's very simple - you need to store editors state in your parent component. Try to use BasicDetails state for this.
Related
React will keep the state around for as long as you render the same component at the same position
in this example, I render 2 Counter components, by checking the checkbox, I control showing/hiding the first Counter component.
export default function App() {
const [showA, setShowA] = useState(true);
return (
<div>
{showA && <Counter />}
<Counter />
<label>
<input
type="checkbox"
checked={showA}
onChange={e => {
setShowA(e.target.checked)
}}
/>
Render the first counter
</label>
</div>
);
}
based on the docs, ** React keeps track of which state belongs to which component based on their place in the UI tree. You can control when to preserve state and when to reset it between re-renders.**
so when I hide/then show the first Counter, its state gets reset, the question is, by hiding and showing the first counter, we are changing the position of the second counter, yet its state doesn't get reset, any help why?
I expected that since the position of the 2nd component changes, then its state will reset as well, yet that did not happen
According to Javascript docs:
operator returns the value of the first falsy operand encountered when
evaluating from left to right, or the value of the last operand if
they are all truthy
I am no expert in Javascript / React JS, but I believe your react UI tree holds values below:
first position: false or <Counter />
second position: always <Counter />
Hence, your second <Counter />'s state will not reset.
I am new with formik and I have a very annoying problem that I am stuck on with days. So basically I have a parent component in which I have a code like this:
const refs=[]
{data.map((v,i) =>
<Child formikRef={refs}}/> )}
And my child component uses formik:
...
<Formik
innerRef={i => props.formikRef.push(i)}
....../>
So my Child component is rendered a couple of times in my parent component, and I need to track the values of each in my parent component. To be more clear, when I click on button that is located in my parent component, I need to have the values from each (I need to pass data from child to parent component). That's why I am using innerRef. The problem is that I am using an array of refs to track values of each rendered , and a new value is added in the array every time I change a field in my form, so I have far more elements in my array than I should have. I think innderRef is triggered each time a change in a field is made, instead of only onSubmit. How I can solve this problem? Pls help.
You could try like below, Here we are passing index value to Child component to have Formik ref in the same place.
const refs=[]
{data.map((v,i) =>
<Child index={i} formikRef={refs}}/>
)}
<Formik
innerRef={props.formikRef[props.index]}
/>
Let's say I've got a Tooltip component that shows & hides depending on whether there is text data for it or not:
{this.state.tooltipText && (
<Tooltip
text={this.state.tooltipText} />
)}
If I wanted to transition this component in and out, I might instead include a boolean on prop and let the component manage transitioning out itself.
<Tooltip
on={this.state.tooltipText !== null}
text={this.state.tooltipText} />
With this setup, though, I lose the text prop right when the transition out should begin. There is very clearly never a state where on is false and I still have access to the text prop.
Is there a good way to handle this scenario?
Should the component keep an internal record of the previous state of tooltipText?
Or is it best to use two properties to track the state of the tooltip (tooltipOn and tooltipText) and never erase this.state.tooltipText?
Should the component keep an internal record of the previous state of tooltipText?
Yes. Otherwise the parent would have to decide when the transition starts / ends, which is not his authority.
Actually componentWillReceiveProps is best for this as you can access the current & next props:
componentWillReceiveProps(nextProps) {
if(this.props.text && !nextProps.text) {
this.fadeOut();
} else if(!this.props.text && nextProps.text) {
this.setState({ text: nextProps.text });
this.fadeIn();
}
}
Then the parent just has:
<Tooltip text={this.state.tooltipText} />
I have a fancy UI in which have a few 'panes' with dividers in between that let you change what each one does. Say I have two different components - a to-do list and a simple text editor. I want you to be able to change the component present in each pane to make a flexible UI. For example, I might want to change the pane on the left from a text editor to a to-do list. Assuming I have a parent element Pane, how could I replace one of its children with another?
<Pane>
<TextEditor /> /* I want to replace that with a <ToDoList /> when I press a button */
<SomeOtherComponentOnTheRight />
</Pane>
I've tried storing React.Children.toArray(this.props.children) in the <Pane />'s state (as this.state.currentChildren), and replacing the element there, but for some reason I can't find a way to get the index of <TextEditor /> in the <Pane />'s this.state.currentChildren because for some reason this.props.children does not preserve children's props, and so I can't transmit data through it.
Sorry if I've overcomplicated this, but I simply want to know how to change a component's children dynamically.
You could check the state in your JSX to change what is displayed such as:
<Pane>
{ this.state.showEditor ? <TextEditor/> : <ToDoList /> }
<SomeOtherComponentOnTheRight />
</Pane>
Elsewhere in your code you would have some button that invokes an onClick event handler that would set the state of 'showEditor' to true/false depending on the previous state.
You can store the selected components in an array or object, then assign the selected component to a variable (just make sure it starts with an uppercase letter) and then use it as a component:
const routes = {
a: TextEditor,
b: ToDoList
};
const ChosenComponent = routes['a']; // select your component and store in variable
return (
<Pane>
<ChosenComponent />
<SomeOtherComponentOnTheRight /> {/* render selected variable as component */}
</Pane>
);
I'm learning to Think in React, but don't understand why the SearchBar in the example needs to have value={this.props.filterText} and checked={this.props.inStockOnly}, the jsFiddle still works without them and it doesn't make sense for props to be passed to the SearchBar as the Search is the one handling user input and making changes to the state. The user input will be reflected in the value of the input without it being set to this.props.filterText so why is it there?
var SearchBar = React.createClass({
handleChange: function() {
this.props.onUserInput(
this.refs.filterTextInput.value,
this.refs.inStockOnlyInput.checked
);
},
render: function() {
return (
<form>
<input
type="text"
placeholder="Search..."
value={this.props.filterText}
ref="filterTextInput"
onChange={this.handleChange}
/>
<p>
<input
type="checkbox"
checked={this.props.inStockOnly}
ref="inStockOnlyInput"
onChange={this.handleChange}
/>
{' '}
Only show products in stock
</p>
</form>
);
}
});
React has concept of controlled components. A controlled component means its value is set by state (And not the other way around i.e. State being set by value of component).
Consider the following example:
class SearchBar extends Component {
constructor(props) {
super(props);
this.state = {term : ''};
}
render() {
return (
<div>
<input value={this.state.term} onChange = {event => this.setState({term : event.target.value}) }/>
</div>
);
}
}
In above example <SearchBar /> is a Controlled Component.
Following will be sequence of events:
You type 'abc' in input field.
At this time value of input field does not change. Rather the State of component is changing because of our code in onChange Event.
As the state of component changes, the component is rendered again. And now the value of component becomes 'abc'.
This concept becomes more important when we use redux, Actions etc.
Because you will be needing the inputted value from the search bar to the upper component. For instance, if you need to filter a collection based on the given value (through search bar), then the filtering will happen on the upper component, not on the search bar. Search bar is only for the input. We put the value of the search bar from the props to make sure that the values are aligned.
The example here depicts the use of controlled input from the parent component. As you see
<input
type="text"
placeholder="Search..."
value={this.props.filterText}
ref="filterTextInput"
onChange={this.handleChange}
/>
Here the value of input box is set to {this.props.value} and in the handlechange function you are the onUserInput function which you check is again passed as a prop to the Searchbar. This calls the handleUserInput function ni the FilterableProductTable component and it sets the states filterText, inStockOnly and only these are passed as props to the Searchbar component. So although you can do without it, but controlled input is the way to go in most cases since we are accepting the value provided by the user and updating the value prop of the <input> component. This pattern makes it easy to implement interfaces that respond to or validate user interactions. i.e. if you want to validate an input field or put restrictions on the input value its easier to do with controlled input