Render function inside component and display in container on button click - javascript

I am newbie in react and javascript and recently start developed an app. I want to render the renderBoard array inside the boardContainer div after i click button . Thx for any answers. This is the code for my component:
class Main extends Component {
constructor(props) {
this.state = {
size: [2, 2],
renderBoard: false,
}
this.renderBoard = this.renderBoard.bind(this);
}
renderBoard() {
var newWorld = [];
var cellRow = [];
for(var i = 0; i < this.state.size[0]; i++) {
for (var j = 0; j < this.state.size[1]; j++){
cellRow.push(<Cell key={[i, j]} />);
}
newWorld.push(<div className="row" key={i}>{cellRow}</div>);
cellRow = [];
}
return newWorld;
}
render() {
return (
<div>
<div className="headerButtons">
<button className="submit" onClick= {this.renderBoard()}>Start</button>
</div>
Generation:
<div className="boardContainer">
{this.renderBoard()}
</div>
</div>
);
}
}
class Cell extends Component {
render() {
return (
<div className="cellContainer"></div>
);
}
}
export default Main;

You need to set the state when you click the button instead of calling your render function. Do something like this:
class Main extends Component {
constructor(props) {
super(props);
this.state = {
size: [2, 2],
renderBoard: false,
}
this.renderBoard = this.renderBoard.bind(this);
}
renderBoard() {
....
}
showBoard => () => {
this.setState({ renderBoard: true });
}
render() {
return (
<div>
<div className="headerButtons">
<button className="submit" onClick={this.showBoard}>Start</button>
</div>
Generation:
<div className="boardContainer">
{this.state.renderBoard ? this.renderBoard() : null}
</div>
</div>
);
}
}
or you could change your renderBoard function instead of using a ternary in your JSX like this:
renderBoard = () => {
if (this.state.renderBoard) {
....
} else {
return null;
}
}
I would also suggest renaming your state variable to be clearer. So maybe instead of renderBoard it would be showBoard. Then you'd use it like if (this.state.showBoard). But that isn't required to make it work.

Related

ReactJS this.props.* is not a function when I pass a function to child component

I write messaging app. When I call the passed functions from the child component, I get the following errors:
TypeError: this.props.createNewChat is not a function.
TypeError: this.props.chooseChat is not a function.
I looked through many topics, tried what I could try and nothing worked.
Will be grateful for any suggestions as I'm a beginner in coding.
Here are parts of my code:
Parent component:
class DashboardComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
chats: [],
email: null,
selectedChat: null,
chatVisible: true
}
this.createNewChat = this.createNewChat.bind(this);
this.chooseChat = this.chooseChat.bind(this);
}
render () {
return (
<main className='dashboard-cont'>
<div className='dashboard'>
<ChatListComponent
newChat={this.createNewChat}
select={this.chooseChat}>
history={this.props.history}
chats={this.state.chats}
userEmail={this.state.email}
selectedChatIndex={this.state.selectedChat}>
</ChatListComponent>
</div>
</main>
)
}
createNewChat = () => {
this.setState({
chatVisible: true,
selectedChat: null
});
}
chooseChat = async(index) => {
await this.setState({
selectedChat: index,
chatVisible: true
});
}
Child component:
class ChatListComponent extends React.Component {
constructor(props) {
super(props);
this.select = this.select.bind(this);
this.newChat = this.newChat.bind(this);
}
render () {
if(this.props.chats.length > 0) {
return (
<main className='listOfChats'>
{
this.props.chats.map((_chat, _index) => {
return (
<div key={_index}>
<div className='chatListItem'
onClick={() => this.select(_index)}
selected={this.props.selectedChatIndex === _index}>
<div className='avatar-circle'>
<h1 className='initials'>{_chat.users.filter(_user => _user = this.props.userEmail)[1].split('')[0]}</h1>
</div>
<div className='text'>
<p id='textLine1'>{_chat.users.filter(_user => _user = this.props.userEmail)[1]}</p>
<br></br>
<p>"{_chat.messages[_chat.messages.length - 1].message.slice(0, 25)}..."</p>
</div>
</div>
</div>
)
})
}
<button className='newChatButton'
onClick={this.newChat}>
New Chat</button>
</main>
);
} else {
return (
<button className='newChatButton'
onClick={this.newChat}>
New Chat</button>
);
}
}
newChat = () => {
this.props.createNewChat();
}
select = (index) => {
this.props.chooseChat(index);
}
};
export default ChatListComponent;
You are passing them as newChat and select
<ChatListComponent
newChat={this.createNewChat}
select={this.chooseChat}>
so these are the names of the properties in the ChatListComponent
You should access them as this.props.newChat and this.props.select
newChat = () => {
this.props.newChat();
}
select = (index) => {
this.props.select(index);
}
You should use
this.props.newChat instead of this.props.createNewChat &
this.props.select instead of this.props.chooseChat
because You are passing them as newChat and select
<ChatListComponent
newChat={this.createNewChat}
select={this.chooseChat}>
history={this.props.history}
chats={this.state.chats}
userEmail={this.state.email}
selectedChatIndex={this.state.selectedChat}>
</ChatListComponent>
In child component
newChat = () => {
this.props.newChat();
}
select = (index) => {
this.props.select(index);
}
You don't have such a property in your component
<ChatListComponent
newChat={this.createNewChat}
select={this.chooseChat}>
history={this.props.history}
chats={this.state.chats}
userEmail={this.state.email}
selectedChatIndex={this.state.selectedChat}>
Your property is newChat and not createNewChat
You need to change the button's onClick to call the properties' method
<button className='newChatButton'
onClick={this.props.newChat}>
New Chat</button>
</main>
and
onClick={() => this.props.select(_index)}

Reactjs: Pressing button to randomize an array

Basically, I am trying to make a card program that would pick five cards out of 52 in random. These cards must not repeat. I have already figured out the randomizer through traditional javascript. However, I am using ReactJs to make a button which if pressed, would create a new set of five cards.
class Reset extends React.Component {
constructor(props) {
super(props);
this.state = {...};
}
handleClick() {...}
render() {
return <button onClick={this.handleClick}>{...}</button>;
}
}
const cards = [
"A♥",
"A♠",
"A♦",
"A♣",
"2♣",
"3♣",
"4♣",
"5♣",
"6♣",
"7♣",
"8♣",
"9♣",
"10♣",
"K♣",
"Q♣",
"J♣",
"2♦",
"3♦",
"4♦",
"5♦",
"6♦",
"7♦",
"8♦",
"9♦",
"10♦",
"K♦",
"Q♦",
"J♦",
"2♥",
"3♥",
"4♥",
"5♥",
"6♥",
"7♥",
"8♥",
"9♥",
"10♥",
"K♥",
"Q♥",
"J♥",
"2♠",
"3♠",
"4♠",
"5♠",
"6♠",
"7♠",
"8♠",
"9♠",
"10♠",
"K♠",
"Q♠",
"J♠"
];
var hand = [];
function in_array(array, el) {
for (var i = 0; i < array.length; i++) if (array[i] == el) return true;
return false;
}
function get_rand(array) {
var rand = array[Math.floor(Math.random() * array.length)];
if (!in_array(hand, rand)) {
hand.push(rand);
return rand;
}
return get_rand(array);
}
for (var i = 0; i < 5; i++) {
document.write(get_rand(cards));
}
ReactDOM.render(<Reset />, document.getElementById("root"));
Basically, what would I have to fill in the parts with "..." in order for the code to rerandomize the pack.
Try something like this, I'm preserving alot of the code you already wrote. You really just have to move that logic into the handler.
Here's the sandbox as well: https://codesandbox.io/s/yv93w19pkz
import React from "react";
import ReactDOM from "react-dom";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
cards: ["A♥", "A♠", "A♦", "A♣", "2♣", "3♣", "4♣", "5♣", "6♣", "7♣", "8♣", "9♣", "10♣", "K♣", "Q♣", "J♣", "2♦", "3♦", "4♦", "5♦", "6♦", "7♦", "8♦", "9♦", "10♦", "K♦", "Q♦", "J♦", "2♥", "3♥", "4♥", "5♥", "6♥", "7♥", "8♥", "9♥", "10♥", "K♥", "Q♥", "J♥", "2♠", "3♠", "4♠", "5♠", "6♠", "7♠", "8♠", "9♠", "10♠", "K♠", "Q♠", "J♠"],
hand: []
}
this.handleClick = this.handleClick.bind(this)
}
handleClick() {
const cards = this.state.cards
const newHand = []
function get_rand(array) {
var rand = array[Math.floor(Math.random() * array.length)];
if (!newHand.includes(rand)) {
newHand.push(rand);
} else {
get_rand(cards);
}
}
for (var i = 0; i < 5; i++) {
get_rand(cards);
}
this.setState({
hand: newHand
})
}
render() {
const { hand } = this.state
return (
<div>
{ hand ? (hand.map((card) => {
return <p>{card}</p>
})) : (
null
)}
<button onClick={this.handleClick}>Randomize
</button>
</div>
);
}
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
Try to declare the cards in the state and update when you click on it

Rendering of react components in unexpected way

I am new to react. I am trying to create a simple todolist using store2.js library. There is a problem in rendering the elements in that to do list.
var React = require('react');
var store = require("store2");
import {Button} from "react-bootstrap";
class CreateToDoList extends React.Component {
componentDidMount() {
this.inputVal="";
}
constructor() {
super();
this.addValueToList = this.addValueToList.bind(this);
this.handleInput=this.handleInput.bind(this);
};
handleInput(e)
{
this.inputValue=e.target.value;
}
addValueToList(){
if(store.has("todoList")===false)
{
store.set("todoList",{count:0})
}
var count=store.get("todoList").count;
count+=1;
var obj={
value:this.inputValue,
isChecked:false
};
store.transact("todoList",function(elements){
elements[count+""]=obj;
elements.count=count
});console.log(store.getAll("todoList"));debugger;
}
render() {
return ( <div>
<input type="input" onChange={this.handleInput}/>
<Button bsStyle="primary" onClick={this.addValueToList}>Add</Button>
<CreateShowPreviousTasks/>
</div>
)
}
}
class todoValues extends React.Component{
componentDidMount(){
this.handleClick=this.handleClick.bind(this);
}
handleClick(){
}
render(){
console.log(this.props)
return(
<div >
<input type="checkbox" checked={this.props.isCheck}></input>
<input type="input">{this.prop.value}</input>
</div>
)
}
}
class CreateShowPreviousTasks extends React.Component {
componentDidMount() {
console.log("here")
}
constructor(){
super();
this.handleClick=this.handleClick.bind(this);
}
handleClick(event)
{
}
render() {
if (store.has('todoList') !== undefined) {
var divElements=[];
this.loop=0;
var count=store.get("todoList").count;
for(this.loop=0;this.loop<count;this.loop++)
{
var obj=store.get("todoList");
obj=obj[count+""];
divElements.push(
<todoValues value={obj.value} key={this.loop+1}/>
)
}
} else {
store.set('todoList',{
count:0
})
}
return (<div>{divElements}</div>
)
}
}
export default CreateToDoList;
The class todoValues adds the div elements of two input buttons wrapped in a div. But the rendering is only done as <todovalues value="as"></todovalues>.
The class CreateShowPreviousTasks which retrieves the list of stored items in the local storage items and passing those values as properties to todoValues and wrapping as in a div.
use the folliwng syntax to render a list
render() {
let todos = store.get("todolist");
return (
<div>
{
Object.keys(todos).map(function(key){
let todo = todos[key];
return (<todoValues value={ todo.value } key={ key }/>)
})
}
</div>
}
Does this answer your question?

Pass state updater clickHandler in React

I have a React app like:
Main.js-
import React, { Component } from 'react';
import _ from 'underscore';
import ApplicationsButtons from '../components/ApplicationsButtons';
let applications_url = 'http://127.0.0.1:8889/api/applications'
export default class Main extends Component {
constructor(props) {
super(props);
this.state = {applications: [], selected_app: 1};
this.updateSelectedApp = this.updateSelectedApp.bind(this);
}
componentDidMount() {
let self = this;
$.ajax({
url: applications_url,
method: 'GET',
success: function(data) {
console.log(data);
let objects = data.objects;
let apps = objects.map(function(object) {
return {name: object.name, id: object.id};
});
console.log(apps);
self.setState({applications: apps});
}
});
}
updateSelectedApp(id) {
this.setState({selected_app: id});
}
render() {
return (
<div>
{this.state.selected_app}
<ApplicationsButtons apps={this.state.applications} />
</div>
);
}
}
ApplicationsButtons.js-
import React, { Component } from 'react';
export default class ApplicationsButtons extends Component {
render() {
var buttons = null;
let apps = this.props.apps;
let clickHandler = this.props.clickHandler;
if (apps.length > 0) {
buttons = apps.map(function(app) {
return (<button key={app.id}>{app.name} - {app.id}</button>);
// return (<button onClick={clickHandler.apply(null, app.id)} key={app.id}>{app.name} - {app.id}</button>);
});
}
return (
<div>
{buttons}
</div>
);
}
}
I want to pass an onClick to the buttons that will change the currently selected app. Somehow, I just got my first infinite loop in React ("setState has just ran 20000 times"). Apparently, when I tried to pass the event handler to be called on click, I told it to keep calling it.
The onClick function should change state.selected_app for the Main component, based on the id for the button that was clicked.
You are not passing the handler as prop.
Here's what you should do:
render() {
return (
<div>
{this.state.selected_app}
<ApplicationsButtons
apps={this.state.applications}
handleClick={this.updateSelectedApp}
/>
</div>
);
}
And in ApplicationButtons:
render() {
var buttons = null;
let apps = this.props.apps;
let clickHandler = this.props.handleClick;
if (apps.length > 0) {
buttons = apps.map(app =>
<button key={app.id} onClick={() => clickHandler(app.id)}>{app.name} - {app.id}</button>);
);
}
return (
<div>
{buttons}
</div>
);
}

React child components won't unmount (reappear)

I'm running stuck on a problem in React (files provided below). I'm trying to generate a set of child components and would like to be able to remove these. I have a state parameter this.state.lipids which contains an array of objects, using these I generate several <DataListCoupledInput /> components inside <RatioCoupledDataLists />. The components are generated and do what their supposed to do with the exception of refusing to be removed.
When I alter my this.state.lipids array by removing an element via removeLipid(index) it triggers a re-render that for some reason magically generates the same <DataListCoupledInput /> component that I just removed.
I tried moving the responsibility up to the parent level of <RatioCoupledDataLists /> but the problem remains.
Please help me.
RatioCoupledDataLists.jsx
class RatioCoupledDataLists extends React.Component {
constructor(props) {
super(props)
this.state = {
lipids: [{value: null, upperLeaf: 1, lowerLeaf: 1}]
}
this.registerLipid = this.registerLipid.bind(this);
this.addLipid = this.addLipid.bind(this);
this.removeLipid = this.removeLipid.bind(this);
}
registerLipid(index, lipid) {
var lipids = [];
lipids = lipids.concat(this.state.lipids);
lipids[index] = lipid;
this.setState({lipids: lipids});
this.state.lipids[index] = lipid;
}
addLipid() {
var mapping = {value: null, upperLeaf: 1, lowerLeaf: 1};
this.setState({lipids: this.state.lipids.concat(mapping)});
}
removeLipid(index) {
var lipids = [];
lipids = lipids.concat(this.state.lipids);
lipids.splice(index, 1);
this.setState({lipids: lipids});
}
render() {
var itemIsLast = function(index, len) {
if (index + 1 === len) {
// item is last
return true;
}
return false;
}
var makeLipidSelects = function() {
var allLipids = [];
for (var i = 0; i < this.state.lipids.length; i++) {
// Add RatioCoupledDataList
allLipids.push(
<DataListCoupledInput
fr={this.registerLipid}
list="lipids"
description="Membrane lipids"
add={this.addLipid}
remove={this.removeLipid}
key={i}
id={i}
isFinal={itemIsLast(i, this.state.lipids.length)}
lipid={this.state.lipids[i]} />);
}
return allLipids;
}
makeLipidSelects = makeLipidSelects.bind(this);
return <div className="category">
<div className="title">Custom Membrane</div>
<DataList data={lipids} id="lipids" />
{makeLipidSelects()}
</div>
}
}
DataListCoupledInput.jsx
class DataListCoupledInput extends React.Component {
constructor(props) {
super(props);
this.state = {
value: this.props.lipid.value,
active: this.props.isFinal,
upperLeaf: this.props.lipid.upperLeaf,
lowerLeaf: this.props.lipid.lowerLeaf
}
this.handleChange = this.handleChange.bind(this);
this.registerLeafletValue = this.registerLeafletValue.bind(this);
this.registerLipid = this.registerLipid.bind(this);
}
registerLipid(lipid) {
this.props.fr(this.props.id, lipid);
}
handleChange(event) {
this.registerLipid({value: event.target.value, upperLeaf: this.state.upperLeaf, lowerLeaf: this.state.lowerLeaf});
this.setState({value: event.target.value});
}
registerLeafletValue(leaflet) {
if (leaflet.name === "upperRatio") {
this.registerLipid({value: this.state.value, upperLeaf: leaflet.value, lowerLeaf: this.state.lowerLeaf});
this.setState({upperLeaf: leaflet.value});
}
if (leaflet.name === "lowerRatio") {
this.registerLipid({value: this.state.value, upperLeaf: this.state.upperLeaf, lowerLeaf: leaflet.value});
this.setState({lowerLeaf: leaflet.value});
}
}
render() {
var canEdit = function() {
if (this.props.isFinal === true) {
return <input onBlur={this.handleChange} list={this.props.list} />;
} else {
return <div className="description">{this.state.value}</div>
}
}
canEdit = canEdit.bind(this);
var addOrRemove = function() {
if (this.props.isFinal !== true) {
return <div className="remove" onClick={() => {this.props.remove(this.props.id)}}><div>-</div></div>;
} else {
return <div className="add" onClick={this.props.add}><div>+</div></div>;
}
}
addOrRemove = addOrRemove.bind(this);
return (
<div className="input-wrap">
<div className="input">
{canEdit()}
</div>
<div className="abundance">
<DynamicNumber
min={this.props.min}
max={this.props.max}
step={this.props.step}
name="lowerRatio"
value={this.state.upperLeaf}
fr={this.registerLeafletValue}
type="linked" />
<div className="to">:</div>
<DynamicNumber
min={this.props.min}
max={this.props.max}
step={this.props.step}
name="upperRatio"
value={this.state.lowerLeaf}
fr={this.registerLeafletValue}
type="linked" />
</div>
{addOrRemove()}
<div className="description">{this.props.description}</div>
</div>
)
}
}

Categories