Pass props parameter to function in React.js when mapping - javascript

When I am mapping through props in a components render method, can I pass in prop values to a function. I have forgotten the correct way to do this. For instance I have a category tag control which just lists some categories with a name, however onClick I want to perform some functionality for the individual category.
var React = require("react");
var CategoryTags = React.createClass({
propTypes: {
categories: React.PropTypes.array.isRequired
},
render: function() {
var categoryRows = this.props.categories.map(function (c, i) {
return (
<button onClick={ this.linkToCategory.bind(name) } >
{c.name}
</button>
);
}.bind(this));
return (
<span>{categoryRows}</span>
);
},
linkToCategory: function (c) {
console.log(c);
}
});
So in this example, while mapping though the categories, I want to pass in the name of the individual category so I can parse the link. Of course it would make sense for the object to have a link property but it didn't in this case.
Example of the categories object being passed in to the component props
categories = [{'name': 'cat1'}, {'name': 'cat2'}];

Binding a function inside .map is not a good idea, because you are always going to pass a new function to the component, even if no other prop changed. That means it will always be re-rendered, even if it doesn't have to be.
But anyways, the problem with your code is that
There is no variable name.
You are using .bind incorrectly.
The call should be
this.linkToCategory.bind(this, c.name)
The first argument passed to bind .bind is the value of this, not the first argument (which you should know since you are using .bind(this) as well).
You can learn more about .bind in the MDN documentation.

You can also return a function from your event handler:
var CategoryTags = React.createClass({
propTypes: {
categories: React.PropTypes.array.isRequired
},
render: function() {
var categoryRows = this.props.categories.map(function (c, i) {
return (
<button onClick={this.linkToCategory(c)} >
{c.name}
</button>
);
}.bind(this));
return (
<span>{categoryRows}</span>
);
},
linkToCategory: function (c) {
return () => {
console.log(c);
}
}
});

Related

React when to use a Function in Props?

In React,
when to use a function in Props?
For example in the code below sometimes you don't use a function in prop,
but sometimes you do.
import React, { useState } from 'react';
const IterationSample = () => {
const input = React.createRef();
const [names, setNames] = useState(
[
{ id: 1, text: 'John' },
{ id: 2, text: 'Jake' },
{ id: 3, text: 'JJ' },
{ id: 4, text: 'Jesus' }
])
const [inputText, setInputText] = useState('');
const [nextId, setNextId] = useState(5);
const onChange = e => setInputText(e.target.value);
const onRemove = id => {
const nextNames = names.filter(name => name.id !== id);
setNames(nextNames);
}
const namesList = names.map(name => <li key={name.id} onDoubleClick={() => onRemove(name.id)}>{name.text}</li>);
const onClick = () => {
const nextNames = names.concat({
id: nextId,
text: inputText
});
setNextId(nextId + 1);
setNames(nextNames);
setInputText('');
input.current.focus();
}
const onEnter = (e) => {
if (e.key === 'Enter') {
onClick();
}
}
return (
<>
<input ref={input} value={inputText} onChange={onChange} onKeyPress={onEnter} />
<button onClick={onClick} >Add</button>
<ul>{namesList}</ul>
</>
);
};
export default IterationSample;
Here you use ()=> a function in onDoubleClick={() => onRemove(name.id)
but, not either in onKeyPress={onEnter} or onChange={onChange}
So when do you use a function in props?
All three of your examples use a function:
onDoubleClick={() => onRemove(name.id)}
onKeyPress={onEnter}
onChange={onChange}
The only difference is that one of them needs to specify an argument to the function (onRemove), whereas the other two do not.
If the arguments being passed by the property (in this case onKeyPress and onChange) are exactly what the function needs, then you just reference the function as-is. But if you need to customize what you're passing to the function (in this case onRemove), you can wrap it in a new function to enclose that customization.
You basically use it when you need to pass extra paremeters (except event itselft).
ex. when you want to pass id in your case you do:
onClick={(e) => onRemove(name.id)}
//or together with the event
onClick={(e) => onRemove(e, name.id)}
Otherwise when you dont need to pass a parameter inside a function, or you only need to pass event you can write it like:
onClick={onRemove}
This
onClick={onClick}
is equivalent to
onClick={(e) => onClick(e)}
For the first case, you are passing the function itself as the props.
For the second case, you are defining an inline function that calls onClick function with the parameter (e).
Generally you can just pass the function itself as the props unless you want to add additional parameters when calling your function. For example,
onClick={(e) => onClick(e, name.id)}
:)
David and Ertan answer are really good and complete.
I will just add two more things.
First, considering reactjs good practice and perfomance do not use arrow function in render.
According to eslint rules eslint rules :
A bind call or arrow function in a JSX prop will create a brand new function on every single render. This is bad for performance, as it may cause unnecessary re-renders if a brand new function is passed as a prop to a component that uses reference equality check on the prop to determine if it should update.
Secondly in the case you need to customize what you're passing to the function (in this case on Remove) you can use an alternative from lodash : partial
the official doc
Here an exemple with your code :
const namesList = names.map(name =>
<li key={name.id}
onDoubleClick={ _.partial(this.onRemove, name.id)}
>
{name.text}
</li>
);
have a good day
Just to clarify, since it seems like you may not be getting quite the beginner-friendly answer that you're looking for:
Something like "onKeyPress" or "onChange" comes with an implicit event. What does that mean? It means that whatever function you tie into that component, it will be triggered with an "event," and that will be the argument of any function that you feed in. In fact, if you look at the functions themselves, you'll notice that they accept an argument, 'e' for event. So, if it helps, you can think of some of those commands as saying
onKeyPress={()=>onEnter(keyPressEvent)}
The events will have some useful data in them, but typically the only thing that we'll care about is something like which key the user pressed, or (in the event of a change) what the new content of an input is.
For something like onDoubleClick, you're giving it some extra data. You can do some fancy footwork with javascript to identify what was double clicked and what its attributes were, but that's a pain in the butt, adds unnecessary code, and tends to obfuscate your intent in whatever it is you were coding. So it's ultimately easier to just feed the function whatever you want it to have.

If render functions should be pure, how the view can get changed based on state?

I am new to react and while going through the tutorials I found this ,
"The render() function should be pure, meaning that it does not modify component state, it returns the same result each time it's invoked, and it does not directly interact with the browser." - https://facebook.github.io/react/docs/react-component.html#reference
I am little confused with this. If render function should return same result each time how can I modify the display based on states ?
For example I have text with edit button. When I click on edit, text-box should appear with content of text and edit button changes to save.
"The render() function should be pure, meaning that it does not modify component state, it returns the same result each time it's invoked, and it does not directly interact with the browser." - https://facebook.github.io/react/docs/react-component.html#reference
is absolutely a correct statement in react. Changing a state cannot be done can't inside a render method. You can access the state inside the render but change. To change the state we use setState function in callback method which set the new state and ready to change anywhere in your component.
Note: setState expects the state to be initialized first. getInitialState is the function for the same and given in example below
eg.
var firstName = 'Anand';
var testExample = React.createClass({
getDefaultProps: function () {
return {
name: 'React',
message: 'This is the default message!'
};
},
getInitialState: function () {
return {
name: this.props.name
};
},
handleNewName: function (name) {
this.setState({
name: name
});
},
render: function () {
var name = this.state.name;
var message = this.props.message;
return (
<div>
<h1>{message}</h1>
<h2>{name}</h2>
</div>
);
}
});
Inside the handleNewName function we have changed the state which then used inside render. Hope this helps

ReactJS - Assign context/owner in 13.x

I am trying to render a child element that has already been initialized and is passed through a prop. My child depends on its context, yet I don't know how to apply that context before a render. Example:
http://jsfiddle.net/5qyqceaj/1/ (code below on React 0.13.3):
var Parent = React.createClass({
childContextTypes: {
test: React.PropTypes.string
},
getChildContext: function() {
return { test: "Working :)" };
},
render: function() {
return (
<div>
<b>Initialized Globally:</b><br/> {this.props.child}
<hr/>
<b>Initialized Inline:</b><br/> <Child/>
</div>
);
}
});
var Child = React.createClass({
contextTypes: {
test: React.PropTypes.string
},
render: function() {
return <div><h1>Context: {this.context.test || "Broken"}</h1></div>;
}
});
var ChildInstance = <Child/>;
React.render(<Parent child={ChildInstance} />, document.getElementById('container'));
In the example above, <Child/> when initialized globally fails to inherit the context passed by Parent.
According to https://github.com/facebook/react/issues/3451#issuecomment-104978224, this is an open issue with React 0.13.x where context is currently assigned via the owner and by 0.14 they will have it inherited from parents.
There are 3 ways I can imagine solving this:
Find a way to assign context within a component upon render or manually switch the owner of an element.
Reconstruct elements by passing in props and tagnames
Wait for 0.14
2 is really un-maintainable for more complicated structures and 3 is the surrender, is there any ways I can achieve 1?
You can use React.cloneElement to re-assign the owner of a React element. It will return a new element. Note that you will need to pass a new ref property, otherwise the previous owner will be preserved.
In your case, you would clone this.props.child in the render method of Parent.

React.js: Passing a callback to a child component and removing a component

Here is a link to the source https://github.com/bengrunfeld/gae-react-flux-todos/tree/master/src/js
In the React tutorial, it shows how to pass a callback to a child component, which can then go ahead and use it.
Say you want to generate a list of Todos, but pass each Todo a callback so that if the delete button is hit on that todo, it will call the function removeTodo in its parent (TodoList) that will remove it from the DOM.
So the problem I'm running into is that in the following example, because of the scope of this.props.data.map, I can't pass this.removeTodo to <Todo>. It errors out as undefined if you try it.
Secondly, even if I could, I am not sure how to go about removing the Todo from the DOM, as I don't have access to a mutable version of state inside TodoList - it is only available as an immutable this.props. If I could alter state, I could then call setState, which would fire a render, which would remove the items from the list.
So how would you solve this?
var TodoList = React.createClass({
removeTodo: function(todo) {
// Remove Todo
// Change the state
// Re-render with setState();
},
render:function(){
var todoNodes = this.props.data.map(function(todo) {
return (
<Todo key={todo.id} id={todo.id} onRemoveTodo={this.removeTodo}>
{todo.title}
</Todo>
);
});
return (
<div className="todoList">
{todoNodes}
</div>
)
}
var Todo = React.createClass({
onDeleteClick: function(todo){
AppActions.deleteTodo({id: todo.target.className});
// Call the `removeTodo` function in `TodoList` (parent) here!
// this.props.onRemoveTodo
},
render:function(){
return (
<div><p>{this.props.children} - <a className={this.props.id} onClick={this.onDeleteClick}>delete</a></p></div>
)
}
});
Looking at your code, since you are following the Flux architecture, you need to emit the CHANGE_EVENT after you delete the todo item in deleteTodo which is present in the store.
deleteTodo: function(todo) {
this.deleteTodoOnServer(todo).done(function(result){
//DELETE your the TodoItem here
AppStore.emitChange(AppConstants.CHANGE_EVENT);
return;
}).fail(function(result){
console.log('fail');
// return 'error in deleteTodoOnServer Ajax call: ' + result;
});
},

How do I keep document.title updated in React app?

Since React doesn't have any builtin way to manage document.title, I used to set it inside componentDidMount of my route handlers.
However now I need to amend the title based on state fetched asynchronously. I started putting assingments into componentDidUpdate, but every now and then I forget to put document.title assignment into some pages, and previous title sticks around until I finally notice it.
Ideally I'd like a way to express document.title declaratively, without having to assign it. Some kind of “fake” component would probably be most convenient, given that I want to be able to specify the document title at several nesting levels:
On top level (the default title);
On page level (for some of the pages, but not all);
Sometimes, on inner component level (e.g. user typing into a field).
Additional requirements:
Title specified in child should override title specified by parent;
Reliable (guarantees cleanup on route change);
Should not emit any DOM (i.e. no hacks with component returning <noscript>);
I'm using react-router but it's better if this component works with other routers too.
Anything I can use?
I wrote react-document-title just for that.
It provides a declarative way to specify document.title in a single-page app.
If you want to get title on server after rendering components to string, call DocumentTitle.rewind().
Features
Does not emit DOM, not even a <noscript>;
Like a normal React compoment, can use its parent's props and state;
Can be defined in many places throughout the application;
Supports arbitrary levels of nesting, so you can define app-wide and page-specific titles;
Works on client and server.
Example
Assuming you use something like react-router:
var App = React.createClass({
render: function () {
// Use "My Web App" if no child overrides this
return (
<DocumentTitle title='My Web App'>
<this.props.activeRouteHandler />
</DocumentTitle>
);
}
});
var HomePage = React.createClass({
render: function () {
// Use "Home" while this component is mounted
return (
<DocumentTitle title='Home'>
<h1>Home, sweet home.</h1>
</DocumentTitle>
);
}
});
var NewArticlePage = React.createClass({
mixins: [LinkStateMixin],
render: function () {
// Update using value from state while this component is mounted
return (
<DocumentTitle title={this.state.title || 'Untitled'}>
<div>
<h1>New Article</h1>
<input valueLink={this.linkState('title')} />
</div>
</DocumentTitle>
);
}
});
Source
I keep track of mounted instances and only use title given to the top DocumentTitle in the mounted instance stack whenever it updates, gets mounted or unmounted. On server, componentWillMount fires but we won't get didMount or willUnmount, so we introduce DocumentTitle.rewind() that returns a string and destroys state to prepare for next request.
var DocumentTitle = React.createClass({
propTypes: {
title: PropTypes.string
},
statics: {
mountedInstances: [],
rewind: function () {
var activeInstance = DocumentTitle.getActiveInstance();
DocumentTitle.mountedInstances.splice(0);
if (activeInstance) {
return activeInstance.props.title;
}
},
getActiveInstance: function () {
var length = DocumentTitle.mountedInstances.length;
if (length > 0) {
return DocumentTitle.mountedInstances[length - 1];
}
},
updateDocumentTitle: function () {
if (typeof document === 'undefined') {
return;
}
var activeInstance = DocumentTitle.getActiveInstance();
if (activeInstance) {
document.title = activeInstance.props.title;
}
}
},
getDefaultProps: function () {
return {
title: ''
};
},
isActive: function () {
return this === DocumentTitle.getActiveInstance();
},
componentWillMount: function () {
DocumentTitle.mountedInstances.push(this);
DocumentTitle.updateDocumentTitle();
},
componentDidUpdate: function (prevProps) {
if (this.isActive() && prevProps.title !== this.props.title) {
DocumentTitle.updateDocumentTitle();
}
},
componentWillUnmount: function () {
var index = DocumentTitle.mountedInstances.indexOf(this);
DocumentTitle.mountedInstances.splice(index, 1);
DocumentTitle.updateDocumentTitle();
},
render: function () {
if (this.props.children) {
return Children.only(this.props.children);
} else {
return null;
}
}
});
module.exports = DocumentTitle;
Take a look at the NFL's react-helmet.
class Layout extends React.Component {
constructor(props){
super(props);
document.title = this.props.title;
}
render(){
return(
<div>
</div>
);
}
}
and then <Layout title="My Title"/> that easy!
Try react-frozenhead, it's actually more sophisticated than react-document-title - it allows us change title, description and anything else in section.
Meanwhile, 3 years have gone! ;-)
If you want to manipulate other page headers than title (like description, canonical, etc.), react-document-meta NPM dependency could be a good thing to use.

Categories

Resources