How to get the values of `textField` in the "Father" component - reactjs

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/

Related

Child not rerendering when parent rerenders on props change

I have a page where I can manage different things like tokens for example. When I choose to create a token, a modal component shows up with list of currently added tokens and form where I have to fill the data in. The issue is - after I add the token, it doesn't immediately show up in the Modal, I have close and re-open modal to see the added token.
managePage.js:
constructor()
{
super()
this.state = { showToken: false, tokensList: []} // control if modal should render
}
showTokenModal = () =>
{
this.getTokens() // fetches and saves tokens list to this.state.tokensList
this.setState({showToken: !this.state.showToken});
}
render()
{
...
return(
...
{this.state.showToken && (<>
<Overlay/>
<Modal onClose={this.showTokenModal} show={this.state.showToken} title={"Create token"}>
{console.log("Refreshed modal")}
<div >
<label htmlFor="title" style={{fontWeight: "bold"}}>Tokens:</label>
{this.state.tokensList.length>0 && (this.state.tokensList.map((t, i) =>
{
return (<div key={i}>{t.tokens.map((token, index) =>
{return <div key={index} >{index+1}: {token} [{t._id}]
(<label style={{color: "red"}} onClick={()=>this.deleteToken(t, token)}><strong>X</strong></label>)</div>})}</div>)
})
)}
<input type="number" name="lvl" defaultValue={2} placeholder="Access Level" onChange={this.handleChange} />
<input name="token" placeholder="Token unique ID" onChange={this.handleChange} />
<div>
<button type="button" onClick={this.showTokenModal}>Cancel</button>
<button type="button" onClick={this.addToken}>Create</button>
</div>
</div>
</Modal>
</>)}
)
}
I only call props functions in Modal component, which won't re-render Modal itself, but will re-render managePage.js, and to my understanding - re-rendering managePage.js should also re-render Modal with the new tokensList, but it doesn't!!!
Modal.js:
import React from "react";
import styles from "../css/Modal.scss";
export default class Modal extends React.Component {
constructor(props) {
super(props);
this.state = {
mystyle: "two"
};
this.timer = this.timer.bind(this);
}
onClose = e => {
this.props.onClose && this.props.onClose(e);
};
timer ()
{
this.setState({mystyle: "modal__close"})
setTimeout(async () => {
this.onClose()
}, 150)
}
render() {
if (!this.props.show) {
return null;
}
let t = this.props.show ? "modal" : "modal__close";
return (
<div className={t} style={styles}>
<div className={this.state.mystyle}>
<h2>{this.props.title}</h2>
<div className="content">{this.props.children}</div>
</div>
</div>
);
}
}
So how can I make Modal refresh when tokens are created/changed?

Forcing a component to update on change

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>
);
}
}

react inner component's state change don't smooth

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>

get value from child component

I am new to React and need some help to my specific situation. I have a top-level app.js where I render
export default class Page extends React.Component {
constructor(props) {
super(props);
this.state = {
currentGuess: '',
historicGuess: '',
result: ''
};
}
handleCurrentGuess(event) {
console.log(event)
this.setState({currentGuess: event.target.value})
}
handleSend() {
console.log(this.state.currentGuess)
}
render() {
return (
<div className="wrapper">
<Header />
<Logic handleCurrentGuess={this.handleCurrentGuess}/>
<Result />
</div>
)
}
}
The component has to be stateful, and I enter the currentGuess value into state.
The <Logic /> looks like this:
export default function Logic(props) {
console.log(props)
return (
<div className="logic">
<form>
<input type="text" onChange={props.handleCurrentGuess}/>
<button onClick={(e) => {
e.preventDefault()
props.handleSend
}}>Send</button>
</form>
</div>
)
}
The issue now is that I cannot find documentation on how to pass both pass the function on to the AND get returned a value from the input. Most docs show onChange via the input directly, but I want to fetch the value ONLY when someone clicks on the submit button (or hits enter). So,
how do I pass the correct function to the child, and how do I get the text value back on button press within the Logic component?
If you want to console.log the state right now (for testing purposes obviously) here is the two problems with your code.
First, you are not passing your handleSend function as a prop to Logic component.
Second, on your button, you are not invoking this handleSend function in your onClick handler.
Here is a working example.
const Logic = props => (
<div className="logic">
<form>
<input type="text" onChange={props.handleCurrentGuess} />
<button onClick={props.handleSend}>Send</button>
</form>
</div>
);
class Page extends React.Component {
state = {
currentGuess: '',
historicGuess: '',
result: ''
};
handleCurrentGuess = event =>
this.setState({ currentGuess: event.target.value })
handleSend = (e) => {
e.preventDefault();
console.log(this.state.currentGuess)
}
render() {
return (
<div className="wrapper">
<Logic
handleCurrentGuess={this.handleCurrentGuess}
handleSend={this.handleSend} />
</div>
)
}
}
ReactDOM.render(<Page />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
I slightly changed the code. Use some arrow functions so no need to .bind them, remove the unnecessary constructor, use class-fields, etc. I also used the function reference for onClick in the button.

how to pass argument in props using functional component

how to pass an argument in props using functional component, here I had given my worked example code,
Let me explain, My click event will trigger from PopUpHandle when I click on the PopUpHandle I need to get the value from ContentSection component. ContentSection will be the listing, when clicking on each listing want to get the value of the current clicked list. I tried with this code my console printed undefined but I don't know how to handle with functional component.
class mainComponent extends Component {
constructor(props) {
super(props);
this.popTrigger = this.popTrigger.bind(this);
}
popTrigger(data){
console.log(data);
}
render(){
return(
<Popup popTrigger={this.popTrigger} />
)
}
}
export default mainComponent;
Popup component
const PopUpHandle = ({ popTrigger, value}) => <li onClick={popTrigger.bind(this, value)} />;
const ContentSection =({popTrigger, value}) =>(
<div>
{value === 'TEST1' && (
<div>
<PopUpHandle popTrigger={popTrigger} value={value} />
</div>
</div>
)}
{value === 'TEST2' && (
<div>
<PopUpHandle popTrigger={popTrigger} value={value} />
</div>
</div>
)}
</div>
)
const ContentList = (({ items, popTrigger}) => (
<div>
{items.map((value, index) => (
<ContentSection
key={`item-${index}`}
popTrigger={popTrigger}
index={index}
value={value}
/>
))}
</div>
)
);
class example extends Component {
constructor(props) {
super(props);
this.state = {
items: ['TEST1', 'TEST2', 'TEST3', 'TEST4'],
};
this.popTrigger = this.popTrigger.bind(this);
}
popTrigger(){
this.props.popTrigger()
}
render(){
return(
<ContentList popTrigger={this.popTrigger} items={this.state.items} />
)
}
}
export default example;
popTrigger(data){
console.log(data);
}
You didn't pass the data while calling this.props.popTrigger(). In javascript if you didn't pass the arguments, it will consider it as undefined.
The ContentSection component is not passed a value prop and hence its not passed on to the PopUpHandle component. Pass it like
render(){
return(
<ContentSection popTrigger={this.popTrigger} value={"test1"} />
)
}

Resources