how to pass argument in props using functional component - reactjs

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"} />
)
}

Related

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

Return multiple Components depending on array prop

Trying to return multiple action buttons from a ActionButtons component:
export default class ActionButtons extends Component {
render() {
console.log(this.props.actions)
return(
this.props.actions.map((field, i) => {
<div key={field.href}>
<DefaultButton
key={field.href}
text={field.label}
href={field.href}
/>
</div>
})
)
}
}
Calling it from another component with the following code:
const actions = [
{"label": "Go Back", "href":"www.google.com"}
];
<ActionButtons actions={actions} />
On the ActionButtons component if i return just one single button without the map then it works. What am i missing ?
You need to explicitly return your jsx from inside map
//inside render
return this.props.actions.map((field, i) => {
return (
<div key={field.href}>
<DefaultButton
key={field.href}
text={field.label}
href={field.href}
/>
</div>
)
}
When using a jsx block () (the example above returns an array which is also valid) you also need to declare your operations inside curly brackets.
export default class ActionButtons extends Component {
render() {
console.log(this.props.actions)
return (
<>
{
this.props.actions.map((field, i) => {
return (
<div key={field.href}>
<DefaultButton
key={field.href}
text={field.label}
href={field.href}
/>
</div>
)
})
}
</>
)
}
}

Functional Component unable to render return value based on props values

Goal: To implement a Toast Message modal (using Functional Component) which will show or hide based on the props value (props.showToastModal) within the return of ToastModal component
Expected: Using props.showToastModal directly would determine if Toast appears
Actual: Modal does not appear based on props.showToastModal
Here's the code:
Parent Component
class Datasets extends Component {
constructor(props) {
super(props)
this.state = {
showToastModal: false,
toastModalText: ''
}
}
toggleOff = () => {
this.setState({ showToastModal: false, toastModalText: '' })
}
render() {
{this.state.showToastModal && (
<ToastModal
showToastModal={this.state.showToastModal}
toastModalText={this.state.toastModalText}
toggleOff={this.toggleOff}
/>
)}
}
}
Child Component
This works:
const ToastModal = (props) => {
const isOpen = props.showToastModal
return (
<div className={`${css.feedbackModal} ${isOpen ? css.show : css.hide}`}>
{props.toastModalText}
<i
className={`bx bx-x`}
onClick={() => props.toggleOff()}
/>
</div>
)
}
export default ToastModal
But this doesn't (using the props value directly):
const ToastModal = (props) => {
return (
<div className={`${css.feedbackModal} ${props.showToastModal ? css.show : css.hide}`}>
{props.toastModalText}
<i
className={`bx bx-x`}
onClick={() => props.toggleOff()}
/>
</div>
)
}
export default ToastModal
Using a const isOpen = props.showToastModal works as expected instead. I am confused why this happens. Is this is a React Lifecycle issue, or a case where it is bad practice to use props values which may be updated during the render?
Please try destructuring objects
const ToastModal = ({ showToastModal, toggleOff }) => {
return (
<div className={`${css.feedbackModal} ${showToastModal ? css.show : css.hide}`}>
{props.toastModalText}
<i
className={`bx bx-x`}
onClick={toggleOff}
/>
</div>
)
}
export default ToastModal

How to pass Grandchild data into GrandParent Component without passing data to intermediate components in Reactjs?

i have created three components Aboutus,AboutusChild & GrandChild and now i want to pass grandchild state value in my grandparent component that is "Aboutus" component but without using intermediate component(AboutusChild), is it possible in react js without using redux state management library.
i dont' want to use redux right now until some data-communication concept are not clear.
class AboutUs extends Component {
constructor(props) {
super(props)`enter code here`
this.state = {
childState: false
}
}
myMehtod(value) {
this.setState({
childState: value
})
}
render() {
console.log('AboutUs', this.state.childState)
return (
<div>
<h1 className={`title ${this.state.childState ? 'on' : ''}`}>
About us {this.props.aboutusProp}
</h1>
<AboutUsChild />
</div>
)
}
};
class AboutUsChild extends Component {
myMehtod(value) {
this.setState({
childState: value
},
this.props.myMehtodProp(!this.state.childState)
)
}
render() {
console.log('AboutUsChild', this.state.childState)
return (
<div>
<h1 className={`title ${this.state.childState ? 'on' : ''}`}>About Us Child</h1>
<GrandChild>
without function
</GrandChild>
<h1>About Us Child</h1>
<GrandChild Method={true} myMehtodProp={this.myMehtod.bind(this)} />
</div>
)
}
};
class GrandChild extends Component {
constructor() {
super()
this.state = {
TitleClick: false
}
}
TitleClick() {
this.setState(
{
TitleClick: !this.state.TitleClick
},
this.props.myMehtodProp(!this.state.TitleClick)
)
}
render() {
console.log('GrandChild', this.state.TitleClick)
return (
<div>
{this.props.Method ?
<h1 onClick={this.TitleClick.bind(this)} className={`title ${this.state.TitleClick ? 'on' : ''}`}>Grand Child</h1>
: null}
<h1>{this.props.children}</h1>
</div>
)
}
};
There's really no way to pass child state up without passing it to some callback. But frankly speaking this does not seem like a good design choice to me. You should always have common state on top of all components consuming that state. What you can do, is to pass stuff as children:
class SomeComponent extends React.Component {
...
render() {
const { value } = this.state;
return (
<Child value={value}>
<GrandChild value={value} onChange={this.handleValueChange} />
</Child>
);
}
}
const Child = ({ value, children }) => (
<div>
<h1 className={`title ${value ? 'on' : ''}`}>About Us Child</h1>
{children}
</div>
)
const GrandChild = ({ value, onChange }) => (
<div>
<h1 onClick={onChange} className={`title ${value ? 'on' : ''}`}>Grand Child</h1>
</div>
);
This way you got control from parent component of everything. If this is not the way, because you are already passing children, and for some reason you want to keep it this way, you can pass "render" prop:
// JSX in <SomeComponent /> render function:
<Child
value={value}
grandChild=(<GrandChild value={value} onChange={this.handleValueChange} />)
>
Some other children
</Child>
...
const Child = ({ value, grandChild, children }) => (
<div>
<h1 className={`title ${value ? 'on' : ''}`}>About Us Child</h1>
{grandChild}
{children}
</div>
)
If you want to be more fancy and there will more than few levels of nesting, you can always use context (highly recommend reading docs before using):
const someContext = React.createContext({ value: true, onChange: () => {} });
class SomeComponent extends React.Component {
...
render() {
const { value } = this.state;
return (
<someContext.Provider value={{ value: value, onChange: this.handleValueChange }}>
<Children>
</someContext.Provider>
);
}
}
...
const SomeDeeplyNestedChildren = () => (
<someContext.Consumer>
{({ value, onChange }) => (
<h1 onClick={onChange}>{value}</h1>
)}
</someContext.Consumer>
)
I would pick first two, if your structure is not that complex, but if you are passing props deeply, use context.
The only way to do something of this sort without external library would be leveraging React's Context API, although bare in mind that it is not nearly as robust as redux or mobX.

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

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/

Resources