Problem passing down function as a prop in ReactJs - javascript

class ReturnTempPassword extends React.Component {
constructor(props) {
super(props);
console.log(JSON.stringify(this.props));
}
render() {
return (
<div>
{ /* change code below this line */ }
<p>Your temporary password is: <strong>{}</strong></p>
{ /* change code above this line */ }
</div>
);
}
};
class ResetPassword extends React.Component {
constructor(props) {
super(props);
this.pwdGen = this.pwdGen.bind(this);
}
// returns a random string as password
pwdGen(m){
var m = m || 9, str="", r = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';;
for(var i=0; i<m; i++) {
str+= r.charAt(Math.floor(Math.random()*r.length));
}
return str;
}
render() {
return (
<div>
<h2>Reset Password</h2>
<h3>We've generated a new temporary password for you.</h3>
<h3>Please reset this password from your account settings ASAP.</h3>
{ /* change code below this line */ }
<ReturnTempPassword data={"data"} pass={this.pwdGen} />
{ /* change code above this line */ }
</div>
);
}
};
I'm sending down a function as a prop and want to access it in the child component ReturnTempPassword. But data is available as prop but not pass. Not sure what am i doing wrong?
FYI, this is a freecodecamp task, which I'm trying to solve in my own way.
Link to task is here:
Please help me to correct the mistake.

you do everything right
Add a call your function
class ReturnTempPassword extends React.Component {
constructor(props) {
super(props);
console.log(JSON.stringify(this.props));
}
render() {
return (
<div>
{ /* change code below this line */ }
<p>Your temporary password is: <strong>{this.props.pass(5)}</strong></p>
{ /* change code above this line */ }
</div>
);
}
};
class ResetPassword extends React.Component {
constructor(props) {
super(props);
}
// returns a random string as password
pwdGen(m){
var m = m || 9, str="", r = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';;
for(var i=0; i<m; i++) {
str+= r.charAt(Math.floor(Math.random()*r.length));
}
return str;
}
render() {
return (
<div>
<h2>Reset Password</h2>
<h3>We've generated a new temporary password for you.</h3>
<h3>Please reset this password from your account settings ASAP.</h3>
{ /* change code below this line */ }
<ReturnTempPassword data={"data"} pass={this.pwdGen} />
{ /* change code above this line */ }
</div>
);
}
};
And this code this.pwdGen = this.pwdGen.bind(this); are not needed

Update Since other people have provided solutions I can provide mine :)
In the task actually, they don't want you to create a random password text. They just want you to pass a "text" as a prop, named tempPassword. You want to use a function here. This is OK but I don't if it passes the test.
You can use this function in two ways.
You can pass it as a prop to the child.
You can use it directly in the parent.
Is there any specific reason you want to use in the child? I think, no.
So you can use it in the parent like:
<ReturnTempPassword pass={this.pwdGen()} />
and in the child:
<p>Your temporary password is: <strong>{this.props.pass}</strong></p>
As you can see since you can do it without passing your function to your child component. Also, you don't need to bind the function since it is not using this here, also it is not being called with a callback. It just a simple method and also can be totally separate from your class.
I've provided an example below like that. You don't need to pass the function as a prop here. In this way, you can use it anywhere. For example, you can put this function in a file then export it. When you need it, you can import it anywhere easily. This function doesn't need to belong to the class itself.
But, if you want to pass it as a prop, #mariamelior's answer shows how you can do this.
// returns a random string as password
function pwdGen(m) {
var m = m || 9,
str = "",
r = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
for (var i = 0; i < m; i++) {
str += r.charAt(Math.floor(Math.random() * r.length));
}
return str;
}
class ReturnTempPassword extends React.Component {
render() {
return (
<div>
{/* change code below this line */}
<p>
Your temporary password is: <strong>{this.props.pass}</strong>
</p>
{/* change code above this line */}
</div>
);
}
}
class ResetPassword extends React.Component {
render() {
return (
<div>
<h2>Reset Password</h2>
<h3>We've generated a new temporary password for you.</h3>
<h3>Please reset this password from your account settings ASAP.</h3>
{/* change code below this line */}
<ReturnTempPassword data={"data"} pass={pwdGen()} />
{/* change code above this line */}
</div>
);
}
}
ReactDOM.render(<ResetPassword />, 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>

Related

How to create a React class for even and odd numbers

**I have a code that displays the news of the day. https://ibb.co/QMLY2Kx I have 10 classes named "block". Inside the "block" class there are two classes named "blockText". I need to get two different class names and not the same, I want to get this result "blockText1" and "blockText2". How to do it? **
import React from 'react';
import newsStyle from './News_module.css';
export class News extends React.Component {
render() {
const resultsRender = [];
for (var i = 0; i < this.props.news.length; i += 2) {
resultsRender.push(
<div class="block">
{
this.props.news.slice(i, i + 2).map((news, index) => {
return (
<div class="blockText" key={index}>
<p class="text">{news.title}</p>
{console.log(this.props.news.length)}
</div>
);
}
)
}
</div>
);
}
return (
<div>
<div className="headlineSecond">
<div className="Second">
{resultsRender}
</div>
</div>
</div>
);
}
}
You can use ternary operator for this . Here is an example where i chose the value of class based on the value of index and deciding upon whether it is even or odd
<div class={ index%2 ===0 ? "blockText1": "blockText2" } key={index}>
..... rest of code
</div>

How do I output my tex from an array to the screen dynamically?

Hello I am new to React and building a quote generator. I want to pull out one quote at a time from my array and show it on the screen, however I can only seem to output each quote to the console.
I have:
1.Created an on click handler and function so that when the user clicks my quote array is targeted.
2. In this function I have created a variable to hold my random array index
3. I have console.logged the array index to see if every time the user clicks it the quote appears.
Component and function and click handler, as you can see the Quote Component should return the quote from the array in my opinion but nothing happens:
class Card extends Component {
state = {
quotes: ['"A dream doesn\'t become reality through magic; it takes sweat, determination and hard work."','"You GOT this!"','"To be or not to be that is the question"'];
changeQuoteHandler = (event) => {
const quotes = [...this.state.quotes];
const arrayIndex = quotes[Math.floor(Math.random() * quotes.length)]
console.log(arrayIndex);
this.setState({
quotes: quotes
})
};
render(){
return (
<div className="Card">
<div>
<h2>Random Quote Generator</h2>
<Quote className="QuoteStyle" quote={this.state.quotes.arrayIndex}/>
</div>
<div className="Flex">
<div>
<NewQuoteButton onClick={this.changeQuoteHandler}/>
</div>
</div>
</div>
)
}
};
export default Card;
Quote Componenet :
import React from 'react';
const Quote = (props) => {
return(
<p>{props.quote}</p>
)
};
export default Quot
I would like to print one quote at a time to the screen on click.
You are so close. You can store the arrayIndex that you generate in the state and use it to display the quote. The code would look like something below
class Card extends Component {
state = {
quotes: ['"A dream doesn\'t become reality through magic; it takes sweat, determination and hard work."','"You GOT this!"','"To be or not to be that is the question"'],
selectedIndex: 0,
}
changeQuoteHandler = (event) => {
const quotes = [...this.state.quotes];
const arrayIndex = Math.floor((Math.random() * 10) % quotes.length);
this.setState({
quotes: quotes,
selectedIndex: arrayIndex,
});
};
render(){
return (
<div className="Card">
<div>
<h2>Random Quote Generator</h2>
<Quote className="QuoteStyle" quote={this.state.quotes[this.state.selectedIndex]}/>
</div>
<div className="Flex">
<div>
<NewQuoteButton onClick={this.changeQuoteHandler}/>
</div>
</div>
</div>
)
}
};
export default Card;

How do I give React methods to the onClick handler of an html component?

I'm trying to change the HTML received from a database to respond to custom onClick handlers. Specifically, the HTML I pull has divs called yui-navsets which contain yui_nav page selectors and yui_content page contents. I want to click an li in yui_nav, set that li's class to "selected", set the existing content to display:none, and set the new content to style="".
To do this, I have created a function updateTabs which inputs the index of the chosen yui and the new page number, set that li's class to "selected", set the existing content to display:none, and set the new content to style="". This function works: I tried running updateTabs(2, 3) in componentDidUpdate, and it worked fine, changing the content as requested. I want to assign updateTabs to each of the lis, and I attempt to do so in my componentDidMount after my axios request.
However, I keep getting the error: TypeError: this.updateTabs is not a function. Please help?
Page.js:
import React, { Component } from 'react';
import axios from 'axios';
class Page extends Component {
constructor(props) {
super(props);
this.state = {
innerHTML: "",
pageTags: [],
};
console.log(this.props.url);
}
componentDidMount() {
console.log(this.props.url);
axios
.get(
this.props.db_address + "pages?url=" + this.props.url,
{headers: {"Access-Control-Allow-Origin": "*"}}
)
.then(response => {
this.setState({
innerHTML: response.data[0].html,
pageTags: response.data[1]
});
console.log(response);
// Check for yui boxes, evade the null scenario
var yui_sets = document.getElementsByClassName('yui-navset');
if (yui_sets !== null) {
let yui_set, yui_nav, yui_content;
// Iterate through the navs of each set to find the active tabs
for (var yui_set_count = 0; yui_set_count < yui_sets.length; yui_set_count ++) {
yui_set = yui_sets[yui_set_count];
yui_nav = yui_set.getElementsByClassName('yui-nav')[0].children;
yui_content = yui_set.getElementsByClassName('yui-content')[0].children;
let tab_count;
// Give each nav and tab and appropriate ID for testing purposes
for (tab_count = 0; tab_count < yui_nav.length; tab_count ++) {
yui_nav[tab_count].onclick = function() { this.updateTabs(yui_set_count); }
yui_nav[tab_count].id = "nav-"+ yui_set_count.toString() + "-" + tab_count.toString()
yui_content[tab_count].id = "content-"+ yui_set_count.toString() + "-" + tab_count.toString()
}
}
}
})
.catch(error => {
this.setState({ innerHTML: "ERROR 404: Page not found." })
console.log(error);
});
}
updateTabs(yui_index, tab_index){
// Get all yuis
var yui_sets = document.getElementsByClassName('yui-navset');
let yui_set, yui_nav, yui_content
yui_set = yui_sets[yui_index];
yui_nav = yui_set.getElementsByClassName('yui-nav')[0].children;
yui_content = yui_set.getElementsByClassName('yui-content')[0].children;
// Identify the current active tab
var current_tab_found = false;
var old_index = -1;
while (current_tab_found == false) {
old_index += 1;
if (yui_nav[old_index].className === "selected") {
current_tab_found = true;
}
}
// Identify the new and old navs and contents
var yui_nav_old = yui_nav[old_index]
var yui_nav_new = yui_nav[tab_index]
var yui_content_old = yui_content[old_index]
var yui_content_new = yui_content[tab_index]
// Give the new and old navs and contents their appropriate attributes
yui_nav_old.className = "";
yui_nav_new.className = "selected";
yui_content_old.style = "display:none";
yui_content_new.style = "";
}
render() {
return (
<div className="Page">
<div className="Page-html col-12" dangerouslySetInnerHTML={{__html:this.state.innerHTML}} />
<div className="Page-footer">
<div className="d-flex flex-wrap btn btn-secondary justify-content-around">
{this.state.pageTags.map(function(pageTag){return(
<div className="pd-2" key={pageTag.id}>
{pageTag.name}
</div>
)})}
</div>
<div className="d-flex justify-content-center" >
<div className="p-2">Discuss</div>
<div className="p-2">Rate</div>
<div className="p-2">Edit</div>
</div>
<div className="d-flex justify-content-around App">
<div className="p-2">
Unless otherwise stated, the content
of this page is licensed under <br />
<a href="http://creativecommons.org/licenses/by-sa/3.0/"
target="_blank" rel="noopener noreferrer">
Creative Commons Attribution-ShareAlike 3.0 License
</a>
</div>
</div>
</div>
</div>
)
}
}
export default Page
Instead of function with function keyword use arrow functions and it will be solved as follows
You have
yui_nav[tab_count].onclick = function() { this.updateTabs(yui_set_count); }
But use
yui_nav[tab_count].onclick = () => { this.updateTabs(yui_set_count); }
Use this in componentDidMount method
You have to bind the updateTabs method in the constructor:
constructor(props) {
super(props);
...
this.updateTabs = this.updateTabs.bind(this);
}
You should use arrow functions in order to call this method with the correct contetxt:
yui_nav[tab_count].onclick = () => { this.updateTabs(yui_set_count); }

react throws type error for undefined object before ternary can check if object is undefined

LONG STORY SHORT: I would like for it to load the object in the nested array IF it is not equal to undefined but react throws typeError
I have this component that takes props from a parent component. Essentially I have an array that contains chat information and when I try to access it in this child component I get some very strange behaviour.
for example if I console log(props.conversations) I get my array which looks like this: conversations[{host, members[{ username }], log[{ author, content, timestamp }]}].
if I console log (props.conversations[0]) ill get the first object in that array. But if I console log (props.conversations[0].log) I get undefined. And thats fine because at the start the state will not be defined or contain anything, so I put a ternary operator as shown below in the code props.conversations[props.index].log[0] == null ?
but all i get is TypeError: Cannot read property 'log' of undefined at the ternary function.
Maybe I am not understanding this correctly or maybe it how react functions?
Again I would like for it to load the object in the nested array IF it is not equal to undefined.
Highly appreciate the help. The most important part is the friends component. I only show the other ones to show the state being passed down.
function Friends(props) {
console.log(props.conversations[props.index]);
return (
<div className="friend">
<img className="friendavatar" src={require("./static/bobby.jpg")}></img>
<div className="friendname">{props.username}</div>
<span className="iswatchingtitle"> is watching <strong>{props.watching}</strong></span>
<div className="friendchat" onClick={props.togglechat}>
{props.conversations[props.index].log[0] == null ?
<div>undefined</div>
:
<div>defined!</div>
}
</div>
</div>
)
}
social component
function Social(props) {
return (
<div>
<div className="userquickdash row">
<div className="usernamedash">{props.username}</div>
<div className="logout"><a href="/users/logout" onClick={props.fetchlogout}>logout</a></div>
</div>
<div>
<form className="search-form-flex" method="GET" action="/search">
<input className="user-search" id="search" type="search" placeholder=" Search users..." name="usersearch"></input>
</form>
</div>
<div className='friendchatcontainer' refs='friendchatcontainer'>
{/* Append friends from social bar state (props.friends). For each friend return appropriate object info to build Friends div using Friends(props) function above. */}
{props.friends.map(function(friend, index) {
// Shortens length of video title if length of string is over 48.
let friendWatching = function friendWatchingLengthSubstring() {
if (friend.watching.length > 57) {
let friendWatching = friend.watching.substring(0, 54) + '...';
return friendWatching;
} else {
friendWatching = friend.watching;
return friendWatching;
}
};
return (
<Friends username={friend.username}
watching={friendWatching()}
key={index}
index={index}
togglechat={props.togglechat}
conversations={props.conversations}
/>
)
})}
</div>
</div>
)
}
socialbar component
class Socialbar extends Component {
constructor(props) {
super(props);
this.state = { isLoggedIn: (cookies.get('loggedIn')),
sidebarximgSrc: sidebarcloseimg,
sidebarStatus: 'open',
username: cookies.get('loggedIn'),
friends: friends,
users: {},
conversations: [],
};
}
// function to run when mongodb gets information that state has changed.
// test if the current state is equal to new object array.
// then do something.
appendFriends() {
}
componentDidMount() {
if (this.state.sidebarStatus === 'open') {
document.getElementsByClassName('maindash')[0].classList.add('maindashwide');
this.openSideBar();
} else {
document.getElementsByClassName('maindash')[0].classList.remove('maindashwide');
this.closeSideBar();
}
// check for user logged in cookie, if true fetch users.
if (this.state.isLoggedIn) {
this.fetchUsers();
}
this.getFriendConversations();
};
getFriendConversations() {
// build loop function that updates state for conversations based on length of friends array in state.
var conversationsArray = this.state.conversations;
for (var i = 0; i < friends.length; i++) {
console.log(aconversationbetweenfriends[i]);
conversationsArray.push(aconversationbetweenfriends[i]);
}
this.setState({conversations: conversationsArray});
}
render() {
let sidebar;
const isLoggedIn = this.state.isLoggedIn;
if (!isLoggedIn) {
sidebar = <Login />
} else {
sidebar = <Social username={this.state.username} friends={this.state.friends} fetchlogout={this.fetchlogout} togglechat={this.togglechat} conversations={this.state.conversations} />
}
return (
<div>
<div className="sidebar sidebar-open" ref="sidebar">
<div className="sidebarcontainer">
{sidebar}
</div>
</div>
<div className="sidebarx sidebarxopen" ref="sidebarx" onClick={this.toggleSideBar}>
<img className="sidebaropenimg" src={this.state.sidebarximgSrc} ref='sidebarximg'></img>
</div>
</div>
);
}
};
It is not a good idea to access the element directly before validation.
Use something like this:
props.conversations[props.index] && props.conversations[props.index].log[0]
Tip: User object destructuring and default props.
You need to compare for undefined like this :
{props.conversations[props.index].log[0] === undefined ?
<div>undefined</div>
:
<div>defined!</div>
}
Also, You can go to below link for sandbox running example.
Sandbox link for example to show how you should check for undefined
Hi first of all check your {props.index} print this value. if it is proper then try this out.
{
props.conversations[props.index] ?
props.conversations[props.index].log[0] ? <div>defined!</div>:<div>Undefined</div>
:
<div>Undefined</div>
}
This will check if props.conversations[props.index] is defined then and then only try to process props.conversations[props.index].log[0]. So you will not get TypeError: Cannot read property 'log' of undefined at the ternary function.

React search highlighing

I am trying to make a search that highlights the matching characters within the displayed list.
I having trouble figuring out how I can add a DOM node within a list as it is being created/updated. The following code is where I got to. I think I understand why its not working (i keep getting 'Stephine Ma[object Object]ks'as the output). I am fairly sure I need to add it as an actual DOM node using .HTMl or .innerHTML but with react im not sure how one would do that.
import React from 'react';
import { Router, Route, Link } from 'react-router';
export default class extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
var divImage = {
backgroundImage : "url(" + this.props.image + ")"
};
var test = this.props.name;
if(this.props.name.indexOf(this.props.filterText) != -1 ) {
var pattern = this.props.filterText.toString();
test = test.replace(pattern, <span className="highlight">+pattern+</span>)
}
return (
<li className="panelItem">
<a className="item-title" style={divImage}>{test}</a>
</li>
);
}
}
Here is an example if you can use indexOf instead of regex matching. Builds all the nodes and wraps them in spans.
https://jsfiddle.net/2zx84koy/
var Hello = React.createClass({
render: function() {
var name = this.props.name;
var startIdx = name.indexOf(this.props.filterText);
var textNodes = <span>{name}</span>
if(startIdx > -1 ) {
textNodes = (
<span>
{name.substring(0, startIdx)}
<span className="highlight">{name.substring(startIdx, startIdx + this.props.filterText.length)}</span>
{name.substring(startIdx + this.props.filterText.length)}
</span>
)
}
return (
<li className="panelItem">
<a className="item-title">{textNodes}</a>
</li>
);
}
});
You can do innerHTML in react but in general its not advised unless you know for sure it would not leave you vulnerable to xss attacks. I put an example below of how to convert your code to that style just for reference.
var test = this.props.name;
if(this.props.name.indexOf(this.props.filterText) != -1 ) {
var pattern = this.props.filterText.toString();
test = test.replace(pattern, '<span class="highlight">' + pattern + '</span>')
}
return (
<li className="panelItem">
<a className="item-title" dangerouslySetInnerHTML={{__html: test}}></a>
</li>
);
I was working on something similar quite recently, I created a library (prelude-extension) and a component (react-selectize) for it, here's a demo, maybe it is what you are looking for.

Categories