Accessing key property value within react dumb component - javascript

How can I access my key property value inside my dumb component?
I have this dumb component:
const TagSummary = ({ tags, highlightTag }) => {
if (!tags) {
return <div />;
}
return (
<div>
{Object.keys(tags).map((tag) => {
return (
<div key={ tag }>
<button type="button" onClick={ highlightTag }>
<pre><{ tag }></pre>
</button>
<p>{ tags[tag] }</p>
</div>
);
})}
</div>
);
};
The method that I pass into it is this:
highlightTag(event) {
event.preventDefault();
console.log(event.target);
}
I want to be able to retrieve the key property in order to perform some other type of logic. How can I retrieve it onClick?

It isn't the best way to do it, instead you should have button be a seperate component where you can pass the onclick and the key as props and then in the button component merge the two together. The quick and dirty way is as follows
<button type="button" onClick={ highlightTag.bind(this, tag) }>
that will make sure that that argument is always provided to the highlightTag function.
The problem with this though is when React checks to see if anything has changed with the component it will always return true because of the function binding in the render method. If you aren't worried about performance you can leave it that way but that is the pitfall of using the binding in the render method

I'm not sure if you need to get the event back in the highlightTag function, but I would do
<button type="button" onClick={ev => {
ev.preventDefault();
highlightTag(tag);
}}>
This will make your function highlightTag more reusable (call this function programmatically, not from a user interaction for instance). It will also decouple implementation detail of TagSummary with its parent.

Related

Svelte export function from component

I wanted to create a component that exposes a function in the parent.
While the Svelte docs point to context="module", that script gets called only once, which would break functionality if I have several instances of the same component.
I found several examples on the internet and they all point to a very handy workaround, defining the export in the component and calling it with dot notation:
// Child.svelte
<script>
let element
export function changeColor() {
console.log(element)
element.style.backgroundColor = "#" + Math.floor(Math.random() * 16777215).toString(16)
}
</script>
<button bind:this={element}>A button</button>
I created a very simple use case, which works when I bind the component to a variable and call it with child.function, but what if I want to call the function from a click on the component itself?
// App.svelte
<script>
import Child from "./Child.svelte"
let child
</script>
<button on:click={() => child.changeColor()}>Click</button> // This works
<Child bind:this={child} on:click={() => child.changeColor()} /> // This doesn't work
I understand the logic behind the first option, however I don't understand why the second option doesn't work!
Should it be the same? I binded the component to a variable, and I am calling it with the same syntax. How can i make it work, without using a second element that calls the function?
<Child bind:this={child} on:click={() => child.changeColor()} />
doesn't work because on:click is not defined, you can easily see this when changing it to on:click={() => console.log('test')}
The reason it doesn't work is because in Svelte events do not 'bubble' out of components, if you want to do that you have to explicitly indicate this in Child.svelte
<button bind:this={element} on:click>A button</button>

How to pass a delete function as props in ReactJS

I am building a recipe app and am having trouble with a delete function.
How it works is you click an "Add Recipe" button and a modal pops up that lets you fill in some input fields for a new recipe. Within that form is an ingredient field and an "Add Ingredient" button. You click "Add" and the ingredient is added to a list of ingredients. It behaves exactly like a todo list, but it is inside of a form component. I also want to be able to remove an ingredient.
My app components are structured like this:
RecipeBook.js (parent)
RecipeCardForm.js (child)
This is the form component located in the parent component:
<RecipeCardForm
handleRemoveIngredients={this.handleRemoveIngredients}
/>
Also in the parent component is the removeIngredient function:
handleRemoveIngredient = id => {
const filteredIngredientList = this.state.ingredientList.filter(ingredient => id !== ingredient.id);
this.setState({ingredientList: filteredIngredientList})
}
In the child component, this is returned inside a <ul> which displays the ingredientsList:
{this.props.ingredientList.map(ingredient => {
return (
<li key={ingredient.id}>
<span>{ingredient.amount}</span>
<span>{ingredient.ingredient}</span>
<span ><button type='button' className='btn btn-danger'
onClick={ this.props.removeIngredient }>X</button></span>
</li>
)
})}
Because of how it's structured, I cannot figure out how to pass the id along which would normally go in the delete function of the delete prop in the parent component.
Normally, I would do something like this:
this.state.items.map(item => {
<List
handleRemoveItem={() => this.handleRemoveItem(item.id)} />
But since nothing is being mapped in the parent component, I have no way to pass the id along.
Because of this, when I log the id in the removeIngredient function, it comes up as undefined. I know its there though. When i log it in the add function, there is an id, and when I log it as I am mapping each ingredient in the child component, it is also there. I can even access it in the onClick of the delete ingredient button:
<button
type='button'
className='btn btn-danger'
onClick={ () => console.log(ingredient.id)}>Delete</button>
That gives me the id too.
I just cannot wrap my mind around how to pass it without mapping in the parent component but there is literally nothing to map in the parent component. And of course passing "this.state.id" just gives me the initial state which is an empty string.
Any help would be much appreciated to help me understand how to do this. Thanks in advance!
I think the solution to your problem would be to pass the ID to the this.props.handleRemoveIngredient function like this:
onClick={this.props.handleRemoveIngredient(ingredient.id)}
In your example you did not hand the function the ingredient id and did call the this.props.removeIngredient function which is not the correct name of the prop / function.
Nevermind, just figured it out.
The solution was indeed onClick={ () => this.props.removeIngredient(ingredient.id) }
My issue was when I was passing the props to the child component handleRemoveIngredient={this.handleRemoveIngredient} I was passing it as a function with no arguments handleRemoveIngredient={() => this.handleRemoveIngredient()} and for some reason that was making the id come back undefined.
Thanks for the help!!
{this.props.ingredientList.map(ingredient => {
return (
<li key={ingredient.id}>
<span>{ingredient.amount}</span>
<span>{ingredient.ingredient}</span>
<span ><button type='button' className='btn btn-danger'
onClick={ () => this.props.removeIngredient(ingredient.id) }>X</button></span>
</li>
)
})}

How to perform a click on element2 when element1 is clicked in React js?

I have two independent elements (not a parent-child).
Is it possible to accomplish the behavior such that StyledDropDownInputAsync is actually clicked on clicking StyledSearchInput.
<StyledSearchInput/>
<StyledDropdownInputAsync searchIcon
className="options"
placeholder="Search"
loadOptions={loadOptions}
onChange={this.handleSelection}
cache={{}}
filterOptions={(options) => (options)}>
</StyledDropdownInputAsync>
Wrap them in a parent component, define a click state, pass the click state to StyledDropdownInputAsync, pass the click action to StyledSearchInput.
const ParentComp = () => {
const [isClicked, setClick] = useState(false);
return (
<>
<StyledSearchInput onClick={() => setClick(true)}/>
<StyledDropdownInputAsync isClicked={isClicked} {...otherProps}>
</StyledDropdownInputAsync>
</>
);
};
This called Lifting State Up in ReactJS.
You can use ref to implement such things,
according to documentation, Refs provide a way to access DOM nodes or React elements created in the render method.
you can read more about refs using this link.
for implementing such functionality, please refer to this code sandbox

React: change modal's content without passing entire DOM elements as a props?

I am new to React and currently I am trying to build a Two-Fact-Auth Modal-like UI in my project.
The Modal looks relatively sample as well.
Title
A message
Modal Content(which can be a inputbox, or one or more drop-down selections, or just displaying string)
A Button
Imagine there are some modals: First one ask you to enter your phone number. After you typed your phone number, it get direct to second modal and second modal will display the phone number you typed and ask you to confirm, and third modal will display other things and so on.
My approach to this is to build a my own modal component using ReactStrap.
export default class ModalControl extends React.Component{
render(){
return(
<div>
<Modal isOpen={this.props.isOpen}>
<ModalHeader >{this.props.title}</ModalHeader>
<ModalBody>
<p>{this.props.message}</p>
<p>{"Content that is change dynamically"}</p>
</ModalBody>
<ModalFooter>
<Button color="info" onClick={() => this.props.clickAction>{this.props.buttonLabel}</Button>
</ModalFooter>
</Modal>
</div>
)
}
}
However, One of the problem I have is to change the modal content. Since the Modal does not know if itself contains a selection, 'A input box', or 'Just a string', what should i do so the ModalControl can take content that is dynamically changing?
My attempt:
I tried to pass entire DOM elements as a string to modal and parse it in the modalControl. However, I have read a SO post that saying passing Dom Elements is not recommended in React.
In my main, i have something like this, but apparently it is not rendering
<ModalControl
isOpen={true}
title={"Code Authentication"}
message={"For your security, we need to verify your identity by sending a code to your phone number"}
buttonLabel={"Verify Code"}
>
<div className="row">
<div className="col-sm-6">
{this.getPhoneList()}
</div>
<div className="col-sm-6">
{this.getMethodList()}
</div>
</div>
</ModalControl>
What i want to achieve: How do I implement a modal class whose modal content that is dynamically changing? Since I am new to React, I am not sure if this is the best practice. If it is not, is there a better approach?
You have to maintain a state which holds variables that can dynamically change.
First you set the state.
constructor(props) {
super(props);
this.state = {
variableX: "default string"
};
}
You can then use React's setState() method to update the state
this.setState({
variableX: "updated string"
});
In your render method you can then access the variable
render() {
const { variableX } = this.state;
return (
<div>{variableX}</div>
);
}
You can also pass the state as a prop to child components
// Parent component
render() {
const { variableX } = this.state;
return (
<div>
<ChildComponent variableX={variableX} />
</div>
);
}
// Child component
render() {
const { variableX } = this.props;
return (
<div>
{variableX}
</div>
);
}
One thing to note is that you never want to mutate the state. You can search many articles regarding this using the terms "react state immutability"
You should read: https://reactjs.org/docs/state-and-lifecycle.html
In a large application where it's more difficult to maintain a state, and where you don't want to keep passing properties all the time, you can consider using Redux with React:
https://redux.js.org/basics/usagewithreact

How do I create a button component that takes parameters on its onClick method in React?

Okay, I've been working at this for 2 days now and I need some help. I previously made a loot logging program for a game I play, and now I want to make it into a Webapp. I'm using React JS to build the app.
I need to make a button component that includes an onClick function and can take parameters. My app renders buttons equal to the length of the loot table for the selected monster (so only the number of buttons needed are made) using a for loop. These buttons need to have an onClick method that takes a value parameter (the value is the index of an array if it matters. The array stores the loot table).
I thought I could just generate the button name by referencing {this.props.name} and the value it needs to pass by using {this.props.value}.
var Button = React.createClass({
render: function() {
return <button type="button" onClick={this.onClick.bind(this,
{this.props.id})}>{this.props.name}</button>
},
onClick: function(value) {
alert(value);
}
});
React.render((<Button name="test" id={1} />),
document.getElementById('example'));
For testing purposes, the button is just drawn in a div with class 'example'.
I've tried to find the examples of something like this working, but I can't find anyone who wants to dynamically create buttons that also pass parameters like I need to do for my webapp. Thank you for any assistance!
Look at the following code you wrote:
return <button type="button" onClick={this.onClick.bind(this,
{this.props.id})}>{this.props.name}</button>
There is an error in the onClick binding. it should look like this:
return <button type="button" onClick={this.onClick.bind(this,
this.props.id)}>{this.props.name}</button>
There are redundant brackets around this.props.id
You could just create a closure for each button's onClick while generating them:
const Button = ({ name, onClick }) => {
return (
<button onClick={onClick}>{ name }</button>
)
}
const List = () => {
return (
<div>
{ [0,1,2,3,4,5,6,7,8,9].map((value, index) => {
const onClick = () => { /* use index here */ }
return <Button onClick={onClick} name={value}/>
}) }
</div>
)
}
as an example. keep in mind I'm using ES6/JSX instead of React.createClass (for my sanity and because it's what I'm familiar with).
Besides, it doesn't make sense for the button to be responsible for handling the onClick logic - it should only be responsible for passing the event back to the relevant "subscribers"

Categories