I'm trying to control inputs with state.
But I'm having a problem with this case which input keep losing it's focus when I hit the keyboard.
See picture below:
https://codesandbox.io/s/how-to-solve-this-7kwj6
export default class App extends React.Component {
constructor(props) {
super(props);
this.onBchange = this.onBchange.bind(this);
}
state = {
a: true,
b: "",
c: ""
};
onBchange(e) {
const thisClass = this;
return (async function() {
await thisClass.setState({ b: e.target.value });
console.log(thisClass.state.b);
})();
}
onCchange = e => {
this.setState({ c: e.target.value });
};
render() {
// this cause problem why?
const Test = ({ b, c, onBchange, onCchange }) => (
<Fragment>
<span>B: </span>
<input value={b} onChange={onBchange} />
<span>C: </span>
<input value={c} onChange={onCchange} />
</Fragment>
);
return (
<div className="App">
{this.state.a && (
<Test
b={this.state.b}
c={this.state.c}
onBchange={this.onBchange}
onCchange={this.onCchange}
/>
)}
</div>
);
}
}
Please let me know if you need more info about this.
Any ideas?
Your app is rerendering those elements on each change because each keystroke causes new props to be passed to <Test/> within your App's render method.
Notice that changing the return method in render to this stops the issue:
<div className="App">
<span>B: </span>
<input value={this.state.b} onChange={this.onBchange} />
<span>C: </span>
<input value={this.state.c} onChange={this.onCchange} />
{/* {this.state.a && (
<Test
b={this.state.b}
c={this.state.c}
onBchange={this.onBchange}
onCchange={this.onCchange}
/>
)} */}
</div>
Related
I am trying to get this componentFunction to re-render with the new data field on the state change that occurs changeValue and I don't know where I'm going wrong.
class Classname extends React.Component {
constructor() {
super();
this.state = {
value: "OriginalString",
};
}
changeValue = (newString) => {
this.setState({ value: newString });
this.forceUpdate();
};
componentFunction = () => {
return (
<div>
<component data={this.state.value} />
</div>
);
};
render() {
return (
<div>
<button
onClick={() => {
this.changeValue("updatedString");
}}
>
Update
</button>
<div className="control-section">
<DashboardLayoutComponent
id="dashboard_default"
columns={5}
cellSpacing={this.cellSpacing}
allowResizing={false}
resizeStop={this.onPanelResize.bind(this)}
>
<PanelsDirective>
<PanelDirective
sizeX={5}
sizeY={2}
row={0}
col={0}
content={this.componentFunction}
/>
</PanelsDirective>
</DashboardLayoutComponent>
</div>
</div>
);
}
}
ReactDOM.render(<Classname />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
Issue
The issue here is a stale enclosure of this.state.value in componentFunction.
Solution(s)
From what I can tell, the content prop of PanelDirective expects anything that returns, or resolves, to valid JSX (JSX attribute). A function callback to provide the content, a React component, or JSX literal all work.
Callback to reenclose updated state. Convert to a curried function that can enclose the current state when component is rendered. When attaching the callback you invoke the first function and pass the state value, the returned function is what PanelDirective will use when calling for the content value.
componentFunction = (data) => () => (
<div>
<component data={data} />
</div>
);
...
<PanelDirective
sizeX={5}
sizeY={2}
row={0}
col={0}
content={this.componentFunction(this.state.value)}
/>
React component. Convert componentFucntion to a React component and pass.
ComponentFunction = ({ data }) => (
<div>
<component data={data} />
</div>
);
...
<PanelDirective
sizeX={5}
sizeY={2}
row={0}
col={0}
content={<ComponentFunction data={this.state.value} />}
/>
JSX literal
<PanelDirective
sizeX={5}
sizeY={2}
row={0}
col={0}
content={
<div>
<component data={this.state.value} />
</div>
}
/>
Also, in case it wasn't obvious, you should remove the this.forceUpdate(); call in the changeValue handler. React state updates are sufficient in triggering the component to rerender.
try to pass in the value as param for the componentFunction, then each time the status value changed, the current component re-render, the trigger the function to re-render the child component using new state value.
class classname extends React.Component {
constructor() {
super();
this.state = {
value: "OriginalString",
};
}
changeValue = (newString) => {
this.setState({ value: newString });
this.forceUpdate();
}
componentFunction = (val) => {
return (
<div>
<component data={val} />
</div>
);
}
render() {
return (
<div>
<button onClick={() => { this.changeValue('updatedString') }}>Update</button>
<div className="control-section">
<DashboardLayoutComponent id="dashboard_default" columns={5} cellSpacing={this.cellSpacing} allowResizing={false} resizeStop={this.onPanelResize.bind(this)} >
<PanelsDirective>
<PanelDirective sizeX={5} sizeY={2} row={0} col={0} content={this.componentFunction(this.state.value)} />
</PanelsDirective>
</DashboardLayoutComponent>
</div>
</div>
);
}
}
class Demo extends React.Component{
constructor (){
super();
this.state = {
list : ['car','map', 'house']
}
}
inputValue(e){
var x = e.target.value;
console.log(x)
}
addValue(){
this.state.list.push();
this.setState({list: this.state.list});
}
render(){
return(
<div>
<input onChange={this.inputValue} type="text"/>
<ul>
{this.state.list.map(item => (
<li>{item}</li>
))}
</ul>
<button onClick={this.addValue.bind(this)}>Add Element</button>
</div>
)
}
}
ReactDOM.render(
<Demo/>,
document.getElementById('test')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.1/umd/react-dom.production.min.js"></script>
<div id="test"></div>
Using my code, how can i push the value from <input onChange={this.inputValue} type="text"/> in list : ['car','map', 'house']. I use for this addValue function, but i can't insert the x variable from inputValue function in push() from addValue function. How to do this using my code?
You need a state value for the text-input so that your addValue() function knows what to use when its time to add a new item. The text state will be updated with anything the user types.
Working demo: https://codesandbox.io/s/magical-feynman-fze1n
import React from "react";
class Demo extends React.Component {
constructor() {
super();
this.state = {
text: "",
list: ["car", "map", "house"]
};
}
inputValue(e) {
this.setState({
text: e.target.value
});
}
addValue() {
const text = this.state.text;
this.setState({ list: [...this.state.list, text] });
}
render() {
return (
<div>
<input onChange={this.inputValue.bind(this)} type="text" />
<ul>
{this.state.list.map(item => (
<li>{item}</li>
))}
</ul>
<button onClick={this.addValue.bind(this)}>Add Element</button>
</div>
);
}
}
export default Demo;
Also, refrain from doing direct state-mutations like this.state.list.push(blah). This is against React principles and can lead to unwanted visual side-effects. If you need to reference an existing state, try to create a copy of it instead. In the case for you list, we use the spread-operator to create a shallow-copy and then added the new item to the array..
Since React is all about small components and reusability consider breaking it up into two separate components... That way, if you need a form anywhere else you can reuse it...
Here is your Demo:
class Demo extends Component {
state = { list: ['car', 'map', 'house'] };
addItem = item => {
this.setState({ list: [item, ...this.state.list] });
};
render() {
return (
<div>
<Form addItem={this.addItem} />
{this.state.list.map((item, index) => (
<div key={index}>{item}</div>
))}
</div>
);
}
}
And here is the Form:
class Form extends Component {
state = { item: '' };
handleChange = event => {
this.setState({ item: event.target.value });
};
handleSubmit = event => {
event.preventDefault();
this.props.addItem(this.state.item);
this.setState({ item: '' });
};
render() {
return (
<form onSubmit={this.handleSubmit}>
<input
type='text'
value={this.state.item}
onChange={this.handleChange}
/>
</form>
);
}
}
Live Demo: https://stackblitz.com/edit/react-611uzp
I have next code:
<Wrapper>
<InputsGroup />
<Controls />
</Wrapper>
In InputsGroup component I have a list of inputs:
// InputsGroup
<>
<input ... />
<input ... />
<input ... />
...
</>
In Controls component I have two buttons: Next and Previously.
// Controls
<>
<button> Prev </button>
<button> Next </button>
</>
The idea, is I need to toggle focus between inputs when click 'Next' or 'Prev' button.
What is the best way to do it, and how can I manage the focus of inputs?
This is how it's looks like:
I would use the local state to track the focused input field.
In the outer componenent:
state = {
focused: 'input_name' || defaultValue
};
handleFocusChange = focused => {
// Use your own logic to change the focus
this.setState({ focused });
};
render() {
const { focused } = this.state;
return (
<Wrapper>
<InputsGroup focused={focused} />
<Controls onClick={this.handleFocusChange} />
</Wrapper>
);
}
Inside your InputsGroup component where you render your inputs:
const { focused } = this.props;
<>
{/* .... */}
<input
{/* ... */}
autoFocus={focused === inputName}
/>
</>
At the end in your Controls component:
const { onClick } = this.props;
<>
<button onClick={() => onClick(/* you need your own logic here, too */)}> Prev </button>
<button onClick={() => onClick(/* you need your own logic here, too */)}> Next </button>
</>
Usually I keep my input field options in a separated constant file, therefore the logic can be super easy to implement. You can use this to walk through the inputs with button click or render the input fields.
Example of a possible field map:
const fields = [
{ name: 'input_name_1', type: 'text', ... },
{ name: 'input_name_2', type: 'text', ... }
];
You can solve this using Refs concept, this is a working solution:-
class App extends React.Component{
constructor(props) {
super(props);
this.state = {
focus: 0
};
this.textInput0 = React.createRef();
this.textInput1 = React.createRef();
this.textInput2 = React.createRef();
}
componentDidMount() {
this.textInput0.current.focus();
}
clickHandler = btnType => {
let focus = this.state.focus;
if (btnType === "prev") {
if (focus !== 0) focus--;
} else {
if (focus !== 2) focus++;
}
this.focusInputField(focus);
this.setState({ focus: focus });
};
focusInputField = id => {
//console.log(id);
switch (id) {
case 0:
this.textInput0.current.focus();
break;
case 1:
this.textInput1.current.focus();
break;
case 2:
this.textInput2.current.focus();
break;
default:
this.textInput0.current.focus();
}
};
render() {
return (
<React.Fragment>
<input placeholder="textInput0" ref={this.textInput0} />
<br />
<input placeholder="textInput1" ref={this.textInput1} />
<br />
<input placeholder="textInput2" ref={this.textInput2} />
<br />
<button onClick={() => this.clickHandler("prev")}>Prev</button>
<button style={{marginLeft: "20px"}} onClick={() => this.clickHandler("next")}>Next</button>
</React.Fragment>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id='root'></div>
I have a wizard that has many forms, at the end of the wizard I want to take them back to the first step. However every form is filled in with the previous values.
I just want to unmount and remount it to wipe everything clean. How do I do this in reactjs?
<StepWizard>
<Step>
<NewComponent/>
</Step>
<Step>
<NewComponent/>
</Step>
<Step>
<NewComponent/>
</Step>
<Step>
<NewComponent/>
</Step>
</StepWizard>
so how would I trigger something to just get "StepWizard" to render in a fresh state?
My components look something like this, I removed code that switches to the next step in the wizard.
export default class NewComponent extends Component {
constructor(props) {
super(props);
}
componentDidMount() {
}
render() {
return (
<Formik
initialValues={{
name: "",
website: "",
}}
validationSchema={Yup.object().shape({
name: Yup.string().required('Company Name is Required'),
website: Yup.string().url('Company Url is Invalid'),
})}
onSubmit={(
values,
{ setSubmitting, setErrors}
) => {
}}
render={({
values,
handleChange,
handleBlur,
handleSubmit,
setFieldValue,
setFieldTouched
}) => (
<form onSubmit={handleSubmit}>
<div className="field">
<label className="label">Name</label>
<div className="control">
<input
className="input"
type="text"
name="name"
maxLength="50"
onChange={handleChange}
onBlur={handleBlur}
value={values.name}
/>
<ErrorMessage name="name"/>
</div>
</div>
<div className="field">
<label className="label">Website</label>
<div className="control">
<Field className="input" name="website" type="text" />
<ErrorMessage name="website"/>
</div>
</div>
</form>
)}
/>
);
}
}
I am using Mbox State Tree, so I could store something in my store that could be used to trigger whatever needs to be triggered to cause a reset.
Edit
I should mention that I am using this plugin: https://github.com/jcmcneal/react-step-wizard
So I am not sure if stopping a step from rendering is an option, also then that would mean I would have to handle the previous step state everytime.
I am more looking for something that just blows away everything if possible as I spent already too much time on this area and don't want to rework tons.
Highlighting the above methods you can also do something like this. Lift the default state into an object that can be pre-filled by whatever, hydrate it into the state and then when you call a reset you can control how much you reset the state back to. This is a very generic example but it's one way to overcome your issue.
Click here to view a working example
import React from "react";
import ReactDOM from "react-dom";
// generic stage renderer
const Stage = ({ step, currentStep, children }) => {
return step === currentStep ? <div>{children}</div> : null;
};
// generic input controller
const Input = ({ stateKey, value, handleOnChange }) => (
<input
value={value}
onChange={evt => handleOnChange(stateKey, evt.target.value)}
/>
);
// default state that is used to reference
const defaultState = {
firstName: '',
lastName: '',
// default state can also be prefilled with data..
telephoneNumber: '0123456789',
}
class App extends React.Component {
state = {
step: 1,
...defaultState
};
handleStateUpdate = (key, value) => {
this.setState({
[key]: value
});
};
incrementStep = () => {
if (this.state.step < 3) {
this.setState({
step: this.state.step + 1
});
}
};
goBack = () => {
const { step, lastName, telephoneNumber } = this.state;
this.setState({
step: 1,
// always reset this one
firstName: defaultState.firstName,
// only reset this one if it's step 3
lastName: step > 2
? defaultState.lastName
: lastName,
// last step blargh, let's reset anyway
telephoneNumber: step === 3
? defaultState.telephoneNumber
: telephoneNumber,
});
}
render() {
const { step, firstName, lastName, telephoneNumber } = this.state;
return (
<div>
{JSON.stringify(this.state)}
<h1>Step Wizard - {step}</h1>
<Stage step={1} currentStep={step}>
<Input
stateKey="firstName"
value={firstName}
handleOnChange={this.handleStateUpdate}
/>
</Stage>
<Stage step={2} currentStep={step}>
<Input
stateKey="lastName"
value={lastName}
handleOnChange={this.handleStateUpdate}
/>
</Stage>
<Stage step={3} currentStep={step}>
<Input
stateKey="telephoneNumber"
value={telephoneNumber}
handleOnChange={this.handleStateUpdate}
/>
</Stage>
<button onClick={this.goBack}>Go Back to Step 1</button>
<button onClick={this.incrementStep}>Next</button>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
You can achieve that by storing the current state of the wizard in, you guessed it, state object. That state and actions to mutate it can be passed to children as props. After that, when you need to reset the wizard, you just reset the state.
Here's an oversimplified example:
class StepWizard extends React.Component {
constructor(props) {
super(props);
this.state = {
step1: {},
step2: {}
};
}
setStep(step, data) {
this.setState({ `step${ step }`: data });
}
resetWizard() {
this.setState({
step1: {},
step2: {}
});
}
render() {
return (
<React.Fragment>
<Step
data={ this.state.step1 }
setData={ (data)=> this.setStep(1, data) }
/>
<Step
data={ this.state.step2 }
setData={ (data)=> this.setStep(2, data) }
/>
</React.Fragment>
);
}
}
Now, call resetWizard whenever you'll need to reset the wizard.
How about creating a Step object that would have the render logic for each step? I understand your use case correctly, since you would want to render only one step at a time why not only render which is relevant at that particular step?
Something like below.
class Wizard {
constructor(props) {
super(props);
this.stepMap = {
first: <FirstStep />,
second: <SecondtStep />,
third: <ThirdStep />,
fourth: <FourthStep />
}
this.state = {
activeStep: "first"
}
}
changeStep = (stepId) => {
this.setState({activeStep: stepId});
}
render() {
const activeStepCmp = this.stepMap[this.state.activeStep];
return (
<StepWizard>
{activeStepCmp}
</StepWizard>
)
}
}
I set a material-ui/TextField in my user-defined component. The user-defined component is named LabelTextField. I render several LabelTextField in my user-defined component which is named TextList. My question is how to get the values of textField in the TextList component.
A button is next to the TextList component in the View component. I will save all the TextField values when someone clicks the button.
I will post a network request in the TextList component to save the value to the backend.
I am using Redux. Does every material-ui/TextField should dispatch the value in the onChange callback function?
The onChange is at the bottom of this website:
http://www.material-ui.com/#/components/text-field
My central code:
LabelTextField:
textChangeFun = (value) => {
}
render() {
return (
<div>
<div style={{fontSize:0}}>
<div style={inlineStyle}>
<FlatButton disableTouchRipple={true} disabled={true} label={this.props.labelValue} />
</div>
<div style={inlineStyle}>
<TextField
hintText={this.props.textValue}
/>
</div>
</div>
</div>
);
}
TextList:
render(){
return (
<div>
{demoData.map((item,id) =>
<LabelTextField key={id} labelValue={item.label} textValue={item.text} ></LabelTextField>
)}
</div>
)
}
You need to give LabelTextField a handler for the change event.
class LabelTextField extends React.Component {
onChange(e) {
this.props.onChange({ id: this.props.id, value: e.currentTarget.value })
}
render() {
return (
<div>
<div style={{fontSize:0}}>
<div style={inlineStyle}>
<FlatButton disableTouchRipple={true} disabled={true} label={this.props.labelValue} />
</div>
<div style={inlineStyle}>
<TextField
hintText={this.props.textValue}
onChange={this.onChange.bind(this)}
/>
</div>
</div>
</div>
);
}
}
class TextList extends React.Component {
constructor() {
super();
this.state.textFields = {}; // TODO: get initial state from demoData
this.onTextFieldChange = this.onTextFieldChange.bind(this);
}
onTextFieldChange = ({ id, value }) {
const { textFields } = this.state;
textFields[id] = value;
this.setState({ textFields });
}
render(){
return (
<div>
{demoData.map((item,id) =>
<LabelTextField key={id} labelValue={item.label} textValue={item.text} onChange={this.onTextFieldChange} ></LabelTextField>
)}
</div>
)
}
}
This way any time a textField changes, it causes the onTextFieldChange handler to be called and the state of TextList to update.
If you have a more complicated situation, you might consider using redux or even http://redux-form.com/6.5.0/