render function automatically trigger onClick props function issue in React? - javascript

In parent component have one child component. In Child component I have one props with click function. In that click function, triggering before click while rendering component. I need to restrict that click function in render. Check below my code...
I have parent component is APP.
import React, { Component } from "react";
import { withRouter } from 'react-router-dom';
import { connect } from "react-redux";
import { Pagination } from './../Common/index';
class App extends Component {
handlePageination = (current) => {
console.log('trigger')
}
render() {
return (
<main className="main-block">
<Pagination total={userList.length>0 ? totalCount : 0} range={10} initial={0} rowClick={this.handlePageination} count={currentPageNumber} />
</main>
)
}
}
export default App;
Child component...
import React, { Component } from "react";
import ReactPaginate from 'react-paginate';
import { ORIGIN_PATH } from "./../../../utilities/consts";
import "./Pagination.css";
class Pagination extends Component {
handlePageClick = (e) => {
this.props.rowClick(e.selected)
}
render() {
const { range, initial } = this.props;
let { count, total } = this.props;
count = count + 10;
total = parseInt(total, 10);
if(count===0 && total < 10) {
count = total
} else if(count >= total) {
count = total
}
return(
<div className="pagination-block">
<p>Showing {count} out of {total} results</p>
{<ReactPaginate previousLabel={<img src={ORIGIN_PATH + "/images/icons/polygon-prev-icon#3x.png"} alt="Prev" />}
nextLabel={<img src={ORIGIN_PATH + "/images/icons/polygon-next-icon#3x.png"} alt="Next" />}
breakLabel={...}
breakClassName={"break-me"}
pageCount={total/range}
marginPagesDisplayed={2}
pageRangeDisplayed={range}
onPageChange={this.handlePageClick}
containerClassName={"pagination"}
subContainerClassName={"pages pagination"}
initialPage={initial}
activeClassName={"active"} />}
</div>
)
}
}
export default Pagination;

change this in your child component while your using click function inside render user arrow function
onPageChange={(e) => this.handlePageClick()}

In your Pagination component render
Change
onPageChange={this.handlePageClick}
To
onPageChange={e => this.handlePageClick(e)}
Corrected code below
import React, { Component } from "react";
import ReactPaginate from 'react-paginate';
import { ORIGIN_PATH } from "./../../../utilities/consts";
import "./Pagination.css";
class Pagination extends Component {
handlePageClick = (e) => {
this.props.rowClick(e.selected)
}
render() {
const { range, initial } = this.props;
let { count, total } = this.props;
count = count + 10;
total = parseInt(total, 10);
if(count===0 && total < 10) {
count = total
} else if(count >= total) {
count = total
}
return(
<div className="pagination-block">
<p>Showing {count} out of {total} results</p>
{<ReactPaginate previousLabel={<img src={ORIGIN_PATH + "/images/icons/polygon-prev-icon#3x.png"} alt="Prev" />}
nextLabel={<img src={ORIGIN_PATH + "/images/icons/polygon-next-icon#3x.png"} alt="Next" />}
breakLabel={...}
breakClassName={"break-me"}
pageCount={total/range}
marginPagesDisplayed={2}
pageRangeDisplayed={range}
onPageChange={e => this.handlePageClick(e)}
containerClassName={"pagination"}
subContainerClassName={"pages pagination"}
initialPage={initial}
activeClassName={"active"} />}
</div>
)
}
}
export default Pagination;

Related

Generating inputs in a React component

I am trying to generate inputs on a button click and the amount of inputs is generated by a random number. Here is what I have so far, but it isn't working. I am very confused and feel like it should be working. I am not sure what I am missing. Any help would be very much appreciated.
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import { Image } from './Image.js'
import { Button } from './Button.js'
import { images } from './assets/images.js'
import { Countdown } from './Countdown.js'
import { DisplayCount } from './DisplayCount.js'
import { Inputs } from './Inputs.js'
class Game extends React.Component{
constructor(props){
super(props)
this.timer = null
this.state = {
currentImg: 0,
timer: null,
ranNum: null
}
this.handleClick = this.handleClick.bind(this)
}
countdownClock = async (newRanNum) => {
const startingNum = newRanNum * 20;
for(let i = startingNum; i >= 0; i--) {
await new Promise(resolve => {
this.timer = setTimeout(() => {
this.setState({
timer: i
})
resolve()
}, 1000)
});
}
}
generateInputs = (newRanNum) => {
const inputs = []
for(let i = 1; i <= newRanNum; i++){
inputs.push(
<Inputs type='text' className='textInputs' />
)
}
return inputs;
}
handleClick(){
clearTimeout(this.timer)
let newRanNum = Math.floor(Math.random() * 20);
this.countdownClock(newRanNum)
this.generateInputs(newRanNum)
let current = this.state.currentImg;
let next = ++current % images.length;
this.setState({
currentImg: next,
ranNum: newRanNum
})
}
render(){
let src = this.state.currentImg;
return(
<div>
<Countdown name={'Countdown: '} countdown={this.state.timer} />
<DisplayCount name='Word Count: ' count={this.state.ranNum} />
<Image src={images[src]} />
<Button onClick={this.handleClick} />
<div>
<ul>
{this.generateInputs()}
</ul>
</div>
</div>
)
}
}
ReactDOM.render(
<Game />,
document.getElementById('root')
);
Inputs component:
import React from 'react'
export const Inputs = (props) => {
return (
<li className={props.className}>
<input value={props.value} />
</li>
)
}
I believe the issue is here...
generateInputs = (newRanNum) ...
and here...
{this.generateInputs()}
You're input rendering function is expecting a parameter it's not getting, and since that returns as undefined the loop never runs. :)
In general, it is most common to separate state (eg, number of inputs) from the rendering, and only have the rendering respond to state. Perhaps you had originally intended generateInputs() to produce the array, and not render them (?)
The first thing is your generateInputs() function takes a parameter for number of inputs you wanted to render but you are not passing any parameter to this.generateInputs()
The second thing is you are returning an array from this function but not mapping your array in the render function, So do in this way.
this.generateInputs(10) // pass parameter here
this.generateInputs(10).map((item, index)=>item)
In this Snack, I have done the same thing but with react-native
https://snack.expo.io/#waheed25/smiling-carrot

How to update one child component alone in React Js

I was making react version of game Bingo. I added 25 buttons which are the child components.
Initially i have empty values in each button. Then on each click, i was trying to update the clicked button's values from 1 to 25.
But when i click on one button, to update the button label, all button's values are getting updated. Can anyone suggest the reason behind that?
App.js
import React from "react";
import "./styles.css";
import GameContainerOne from "./GameContainerOne";
export default function App() {
return (
<div className="App">
<GameContainerOne />
</div>
);
}
GameContainerOne.js
import React from "react";
import ButtonBox from "./ButtonBox";
class GameContainerOne extends React.Component {
constructor(props) {
super(props);
this.state = {
btnLabel: 0
};
}
handleClicked = () => {
if (this.state.btnLabel < 25) {
this.setState({ btnLabel: ++this.state.btnLabel });
console.log("after", this.state.btnLabel);
}
};
render() {
let menuItems = [];
let key = 0;
for (let i = 0; i < 5; i++) {
for (let j = 0; j < 5; j++) {
let index = "" + i + j;
// console.log(index)
key++;
menuItems.push(
<ButtonBox
key={key}
index={index}
value={this.state.btnLabel}
handleClicked={this.handleClicked.bind(this)}
/>
);
}
}
return <div className="wrapper">{menuItems}</div>;
// this.handleButtonBox()
}
}
export default GameContainerOne;
ButtonBox.js
import React from "react";
import Button from "#material-ui/core/Button";
class ButtonBox extends React.Component {
constructor(props) {
super(props);
this.state = {
initialBtnColor: "Default",
selectedBtnColor: "Primary"
};
}
handleClick = () => {
// console.log("before",this.state.btnLabel)
this.setState({ initialBtnColor: "Primary" });
return this.props.handleClicked;
};
render() {
console.log("Key=", this.props);
// const { index } = this.props.index;
console.log("Key=", this.props.index);
return (
<div>
<Button
variant="contained"
color={this.state.initialBtnColor}
onClick={this.props.handleClicked}
>
{this.props.value}
</Button>
</div>
);
}
}
export default ButtonBox;
Please find the codesandbox link : https://codesandbox.io/s/bingo-game-glk8v
Move the btnLabel state into the ButtonBox.
Example (using hooks):
// ButtonBox.js
import React from "react";
import Button from "#material-ui/core/Button";
function ButtonBox(props) {
const [buttonColor, setButtonColor] = React.useState("Default");
const [value, setValue] = React.useState(props.startValue);
return (
<div>
<Button
variant="contained"
color={buttonColor}
onClick={() => {
setValue(value + 1);
setButtonColor("Primary");
props.handleClicked(props.index);
}}
>
{value}
</Button>
</div>
);
}
export default ButtonBox;
// GameContainerOne.js
import React from "react";
import ButtonBox from "./ButtonBox";
function GameContainerOne(props) {
const handleClicked = React.useCallback(btnIndex => {
// Called after a button is clicked
}, []);
let menuItems = [];
let key = 0;
for (let i = 0; i < 5; i++) {
for (let j = 0; j < 5; j++) {
let index = "" + i + j;
key++;
menuItems.push(
<ButtonBox
key={key}
index={index}
startValue={0}
handleClicked={handleClicked}
/>
);
}
}
return <div className="wrapper">{menuItems}</div>;
}
export default GameContainerOne;
You change the state on click which is the source of labels for all the buttons.
You could have a separate state for all the buttons to avoid that and changed them based on the index on the button clicked.

How can I use react useContext , to show data from context, in the same component

I am creating a react wizard component, and want to pass data × etween parent and children using context×¥
So I created a wizard, context, provider, and custom hook, but the issue is that if I try to use the context, on the wizard component, it does not show the correct info
(see https://codesandbox.io/embed/wizardwitcontext-rfpui )
How to make it so that I can rely on data in context on the wizard itself so I can transfer the login to the custom hook?
useWizard.js:
import React, { useContext, useEffect } from "react";
import { WizardContext } from "./WizardContext";
const useWizard = () => {
const [state, setState] = useContext(WizardContext);
function setMaxSteps(maxSteps) {
setState(state => ({ ...state, maxSteps }));
}
function moveToStep(index) {
if (state.maxSteps && state.maxSteps > index) {
setState({ ...state, currentStep: index });
return index;
}
return state.currentStep;
}
function back() {
if (state.maxSteps) {
if (state.currentStep > 0) {
setState({ ...state, currentStep: state.currentStep - 1 });
window.scrollTo(0, 0);
}
}
}
//move back a step
function next() {
if (state.currentStep < state.maxSteps) {
setState({ ...state, currentStep: state.currentStep + 1 });
window.scrollTo(0, 0);
}
}
return {
setMaxSteps,
moveToStep,
back,
next,
maxSteps: state.maxSteps,
currentStep: state.currentStep,
state
};
};
export default useWizard;
Wizard.jsx:
const { state, currentStep, back, next, maxSteps, setMaxSteps } = useWizard();
return (
<div className="wizard">
<WizardProvider
maxSteps={React.Children.count(props.children)}
currentStep={0}
>
{/* <div className="wizard__upper">
<ProgressIndicator currentIndex={selected} onChange={onClick}>
{steps}
</ProgressIndicator>
<Button id="wizardCloseBtn" kind="ghost" onClick={onClose}>
<Icon icon={iconHeaderClose} />
</Button>
</div> */}
<div className="wizard__separator" />
<div className="wizard__content">
{`in wizard: cur=${currentStep}, max=${maxSteps}`}
{/* {getContentAt(0)} */}
{stepContentWithProps}
</div>
{/* <div className="wizard__buttons">
{showBack && (
<Link id="back" onClick={back}>
back
</Link>
)}
{showNext && (
<button id="next" onClick={next} kind="secondary">
Next Step
</button>
)}
</div> */}
</WizardProvider>
</div>
);
Step2:
import React, { useState, useContext, useEffect } from "react";
import useWizard from "./useWizard";
function Step2(props) {
const {
currentStep,
moveToStep,
maxSteps,
setMaxSteps,
next,
prev
} = useWizard();
return (
<div>
<p>Step 2</p>
{`in step2 (inner child of wizard): cur=${currentStep} see that cur !== cur from wizard above`}
<br />
<button onClick={() => moveToStep(1)}>
Click me to change current step
</button>
</div>
);
}
export default Step2;
End result is:
in wizard: cur=undefined, max=undefined
p1
in index.js: cur=undefined
Step 2
in step2 (inner child of wizard): cur=0 see that cur !== cur from wizard above
You calling useContext in the same level as the Context.Provider:
function Wizard(props) {
// useWizard calls useContext
const { state, currentStep, back, next, maxSteps, setMaxSteps } = useWizard();
return (
<div className="wizard">
<WizardProvider
maxSteps={React.Children.count(props.children)}
currentStep={0}
>
<div className="wizard__content">
{`in wizard: cur=${currentStep}, max=${maxSteps}`}
</div>
</WizardProvider>
</div>
);
}
You need to change your structure and call useContext within the Provider children.
function Wizard(props) {
// useWizard calls useContext
const { state, currentStep, back, next, maxSteps, setMaxSteps } = useWizard();
return (
// v You trying to get Provider's value here
<div className="wizard">
<WizardProvider
maxSteps={React.Children.count(props.children)}
currentStep={0}
>
// v useContext available within the children
<ComponentA />
<ComponentB />
</WizardProvider>
</div>
);
}
Refer to Context API, useContext.
Context is derived from the nearest provider above the component tree. From the react docs.
const value = useContext(MyContext);
Accepts a context object (the value returned from React.createContext) and returns the current context value for that context. The current context value is determined by the value prop of the nearest above the calling component in the tree.
In this case you have 2 options.
1.You need to wrap your App (index.js) component in the provider.
or
2.Let the Wizard component be the provider and try to use useContext hook in the child components.
Demo: https://stackblitz.com/edit/react-msac8q
Hope this helps
I think Since we can not use useContext() in the same Component as Provider, I think we can do something workaround, I think this will be helpful to use in the Main Components like pages/screens
// This will be your child component
function Wizard(props) {
// useWizard calls useContext
const { state, currentStep, back, next, maxSteps, setMaxSteps } = useWizard();
return (
<div className="wizard">
<div className="wizard__content">
{`in wizard: cur=${currentStep}, max=${maxSteps}`}
</div>
</div>
);
}
// This is your main Page
export default function WizardPage(){
return <WizardProvider
maxSteps={React.Children.count(props.children)}
currentStep={0}>
<Wizard />
</WizardProvider>
}
I Found the solution, thanks to this article: https://dev.to/email2vimalraj/react-hooks-lift-up--pass-down-state-using-usecontext-and-usereducer-5ai0
The solution as described is to create a reducer on the wizard file, and so the wizard has access to its data, and also the childern:
Wizard.jsx
import React, {
useState,
useEffect,
useLayoutEffect,
useContext,
useReducer
} from "react";
import PropTypes from "prop-types";
import "./wizard.scss";
import {
WizardContext,
wizardReducer,
SET_CURRENT_STEP,
SET_MAX_STEPS,
BACK,
NEXT
} from "./WizardContext";
function StepContent(props) {
const { selected, children, ...other } = props;
return (
<li {...other} selected={selected}>
{children}
</li>
);
}
function Wizard(props) {
const { onClose, onChange, pageContentClassName } = props;
function onClick(index) {
dispatch({ type: SET_CURRENT_STEP, currentStep: index });
// setSelected(index);
}
//get the progressBar steps
const steps = React.Children.map(props.children, page => {
const { id, label, description } = page.props;
return <div id={id} label={label} description={description} />;
});
function getContentAt(index) {
return stepContentWithProps[index];
}
const stepsWithProps = React.Children.map(props.children, (step, index) => {
const newStep = React.cloneElement(step, {});
return newStep;
});
const stepContentWithProps = stepsWithProps.map((step, index) => {
const { children } = step.props;
return (
<StepContent key={index} className={pageContentClassName}>
{children}
</StepContent>
);
});
const initialState = {
maxSteps: React.Children.count(props.children),
currentStep: 0
};
const [wizardData, dispatch] = useReducer(wizardReducer, initialState);
return (
<div className="wizard">
<p>This text is in wizard: currentStep={wizardData.currentStep}</p>
<WizardContext.Provider value={{ wizardData, dispatch }}>
<div className="wizard__upper">
<ul currentIndex={wizardData.currentStep} onChange={onClick}>
{steps}
</ul>
</div>
<div className="wizard__separator" />
<div className="wizard__content">{stepsWithProps}</div>
<div>
<button onClick={() => dispatch({ type: BACK })}>Back</button>
<button onClick={() => dispatch({ type: NEXT })}>Next</button>
</div>
</WizardContext.Provider>
</div>
);
}
Wizard.propTypes = {
/**
* Specify the text to be read by screen-readers when visiting the <Tabs>
* component
*/
ariaLabel: PropTypes.string,
/**
* Pass in a collection of <Tab> children to be rendered depending on the
* currently selected tab
*/
children: PropTypes.node,
/**
* Provide a className that is applied to the <PageContent> components
*/
pageContentClassName: PropTypes.string
};
export default Wizard;
WizardContext.jsx
import React, { createContext } from "react";
export const WizardContext = React.createContext(null);
export const SET_MAX_STEPS = "SET_MAX_STEPS";
export const SET_CURRENT_STEP = "SET_CURRENT_STEP";
export const BACK = "BACK";
export const NEXT = "NEXT";
export const SHOW_BACK = "SHOW_BACK";
export const SHOW_NEXT = "SHOW_NEXT";
export function wizardReducer(state, action) {
switch (action.type) {
case SET_MAX_STEPS:
return {
...state,
maxSteps: action.maxSteps
};
case SET_CURRENT_STEP:
if (action.currentStep >= state.maxSteps) return state;
return {
...state,
currentStep: action.currentStep
};
case BACK:
if (state.currentStep === 0) return state;
return {
...state,
currentStep: state.currentStep - 1
};
case NEXT:
if (state.currentStep >= state.maxSteps - 1) return state;
return {
...state,
currentStep: state.currentStep + 1
};
default:
return state;
}
}
Index.js
import React, { useState } from "react";
import ReactDOM from "react-dom";
import "./styles.css";
import Wizard from "./Wizard";
import Cmp2 from "./Cmp2";
function App() {
const [wizardVisible, setWizardVisible] = useState(false);
return (
<div className="App">
<h1>
Wizard: why cant I see currentStep in wizard
<br />
(WORKING NOW!!!)
</h1>
<Wizard>
<div label="ddd">This is step1</div>
<Cmp2 />
<div label="ddd">This is step3</div>
</Wizard>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Cmp2.jsx
import React, { useState, useContext, useEffect } from "react";
import { WizardContext, SET_CURRENT_STEP } from "./WizardContext";
function Cmp2(props) {
const { wizardData, dispatch } = useContext(WizardContext);
return (
<div>
<br />
<p>This is Step 2</p>
{`in step2 (inner child of wizard): cur=${wizardData.currentStep}`}
<br />
<button
onClick={() => dispatch({ type: SET_CURRENT_STEP, currentStep: 1 })}
>
Click me to change current step
</button>
<br />
<br />
</div>
);
}
export default Cmp2;
Now I need to find how to make it accessible, I mean, it works nice, but when I try to create a custom hook (which imports the Context), the context is null, when trying to use the custom hook (which is understandable, since it is called in wizard BEFORE the provider), how to add better functionality here?
here is a working solution (without the hook):
https://codesandbox.io/embed/wizardwitcontext-working-3lxhd

How to load a new component on button click in ReactJs?

I created a main component in ReactJs called MainPage (using Material-UI).
import React from 'react';
import Grid from '#material-ui/core/Grid';
import Button from '#material-ui/core/Button';
import CssBaseline from '#material-ui/core/CssBaseline';
import Card from '#material-ui/core/Card';
import CardContent from '#material-ui/core/CardContent';
import withStyles from '#material-ui/core/styles/withStyles';
const styles = theme => ({
card: {
minWidth: 350,
},
button: {
fontSize: '12px',
margin: theme.spacing.unit,
minWidth: 350
},
extendedIcon: {
marginRight: theme.spacing.unit,
}
});
class MainPage extends React.Component {
constructor() {
super();
}
render() {
const {
classes
} = this.props;
return ( <
React.Fragment >
<
CssBaseline / >
<
Grid container spacing = {
0
}
direction = "column"
alignItems = "center"
justify = "center"
style = {
{
minHeight: '100vh'
}
} >
<
form onSubmit = {
this.handleSubmit
} >
<
Card className = {
classes.card
} >
<
CardContent >
<
Grid item xs = {
3
} >
<
Button variant = "contained"
size = "medium"
color = "primary"
className = {
classes.button
}
type = "submit"
value = "single" >
ButtonA <
/Button> <
/Grid> <
Grid item xs = {
3
} >
<
Button variant = "contained"
size = "medium"
color = "primary"
className = {
classes.button
}
type = "submit"
value = "batch" >
ButtonB <
/Button> <
/Grid> <
/CardContent> <
/Card> <
/form> <
/Grid> <
/React.Fragment>
);
}
}
export default withStyles(styles)(MainPage);
I want to load a new component (either CompA or CompB, depending on which button was clicked - ButtonA or ButtonB) on button click. A new component should completely replace a current component - I mean that it should be loaded in a whole screen (not anywhere next to buttons).
How can I do it?
UPDATE:
I want to replace MainPage component, not just render on top of it.
This is how I load MainPage:
index.js
import React from 'react';
import { render } from 'react-dom';
import MainPage from './components/MainPage';
const View = () => (
<div>
<MainPage/>
</div>
);
render(<View />, document.getElementById('root'));
You can create a different component to handle the state and add an if statement in that component to handle the view that you want to render.
You can see the example here codesandbox.io/embed/6wx2rzjrr3
App.js
import React, { Component } from "react";
import ReactDOM from "react-dom";
import Main from "./Main";
import View1 from "./View1";
import View2 from "./View2";
import "./styles.css";
class App extends Component {
state = {
renderView: 0
};
clickBtn = e => {
this.setState({
renderView: +e.target.value
});
};
render() {
switch (this.state.renderView) {
case 1:
return <View1 />;
case 2:
return <View2 />;
default:
return <Main clickBtn={this.clickBtn} />;
}
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Main.js
import React from "react";
export default props => (
<>
Main view{" "}
<button value={1} onClick={props.clickBtn}>
View 1
</button>{" "}
<button value={2} onClick={props.clickBtn}>
View 2
</button>{" "}
</>
);
View1.js
import React from "react";
export default props => "View 1";
View2.js
import React from "react";
export default props => "View 2";
In your index.js you can use
const View = () => (
<div>
<MainPage condition='something' />
</div>
);
Then in you main page:
class MainPage extends React.Component {
constructor() {
super();
}
myCondition1() {
return (
<Component1 />
);
}
myCondition2() {
return (
<Component2 />
);
}
render() {
const { condition} = this.props;
return (
{condition === 'something' ? this.myCondition1() : this.myCondition2()}
)
}
}
UPDATE
Here's an example with a simple button:
class Condition1 extends React.Component {
render() {
return (
<div>Condition 1</div>
);
}
}
class Condition2 extends React.Component {
render() {
return (
<div>Condition 2</div>
);
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
condition: true
};
this.handleClick = this.handleClick.bind(this);
}
handleClick(condition) {
this.setState( {condition} )
}
render() {
const { condition } = this.state;
return (
<div>
<button onClick={() => this.handleClick(true)}>Condition1</button>
<button onClick={() => this.handleClick(false)}>Condition2</button>
{condition === true ? <Condition1 /> : <Condition2 />}
</div>
)
}
}
ReactDOM.render( < App / > ,
document.getElementById('root')
);
<script src="https://unpkg.com/react#16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom#16/umd/react-dom.development.js" crossorigin></script><div id="root" />

Show the last index in an array when I click (in an array that continues to be updated)

I am near the end of creating my application.
So it is for banks accounts where they ask you to give the first letter of your password, then for example fourth, etc.
I'm tired of counting on my own so I created this app.
But there is the last bug that I don't know how to fix.
So when I press "1" I get "1 - H", and then when I press "4" I want to get:
"1 - H" (clicked before)
"4 - X" (clicked just now)
but instead, I get:
"4 - X" (clicked just now)
"4 - X" (clicked just now)
So it is caused by the way handleResults() function works inside my Input component, but for now it is my only concept how to approach this...
import React, { Component } from 'react';
import TextField from 'material-ui/TextField';
import './style.css';
import Buttons from '../Buttons';
import Results from '../Results';
class Input extends Component {
constructor(props) {
super(props);
this.state = {
password: 'Hh9Xzke2ayzcEUPHuIfS',
selectedButtons: [],
};
this.handleButtonSelectTwo = this.handleButtonSelectTwo.bind(this);
}
handleInputChange(pass) {
this.setState({ password: pass });
}
handleButtonSelectTwo(selected) {
this.setState({
selectedButtons: [...this.state.selectedButtons, selected],
});
}
handleResults() {
return this.state.selectedButtons.map(el => (
<Results key={el} appState={this.state} />
));
}
render() {
return (
<div>
<div className="Input-textfield">
<TextField
hintText="Paste your password here to begin"
value={this.state.password}
onChange={event => this.handleInputChange(event.target.value)}
/>
</div>
<div>
<Buttons
handleButtonSelectOne={this.handleButtonSelectTwo}
array={this.state.password.length}
/>
{this.handleResults()}
</div>
</div>
);
}
}
export default Input;
and here is Results component code:
import React, { Component } from 'react';
import _ from 'lodash';
import Avatar from 'material-ui/Avatar';
import List from 'material-ui/List/List';
import ListItem from 'material-ui/List/ListItem';
import './style.css';
const style = {
avatarList: {
position: 'relative',
left: -40,
},
avatarSecond: {
position: 'relative',
top: -40,
left: 40,
},
};
class Results extends Component {
resultsEngine(arg) {
const { selectedButtons, password } = this.props.appState;
const passwordArray = password.split('').map(el => el);
const lastSelectedButton = _.last(selectedButtons);
const passwordString = passwordArray[_.last(selectedButtons) - 1];
if (arg === 0) {
return lastSelectedButton;
}
if (arg === 1) {
return passwordString;
}
return null;
}
render() {
if (this.props.appState.selectedButtons.length > 0) {
return (
<div className="test">
<List style={style.avatarList}>
<ListItem
disabled
leftAvatar={<Avatar>{this.resultsEngine(0)}</Avatar>}
/>
<ListItem
style={style.avatarSecond}
disabled
leftAvatar={<Avatar>{this.resultsEngine(1)}</Avatar>}
/>
</List>
</div>
);
}
return <div />;
}
}
export default Results;
Anyone has an idea how should I change my code inside handleResults() function to achieve my goal? Any help with solving that problem will be much appreciated.
Buttons component code:
import React from 'react';
import OneButton from '../OneButton';
const Buttons = props => {
const arrayFromInput = props.array;
const buttonsArray = [];
for (let i = 1; i <= arrayFromInput; i++) {
buttonsArray.push(i);
}
const handleButtonSelectZero = props.handleButtonSelectOne;
const allButtons = buttonsArray.map(el => (
<OneButton key={el} el={el} onClick={handleButtonSelectZero} />
));
if (arrayFromInput > 0) {
return <div>{allButtons}</div>;
}
return <div />;
};
export default Buttons;
And OneButton code:
import React, { Component } from 'react';
import RaisedButton from 'material-ui/RaisedButton';
const style = {
button: {
margin: 2,
padding: 0,
minWidth: 1,
},
};
class OneButton extends Component {
constructor() {
super();
this.state = { disabled: false };
}
handleClick() {
this.setState({ disabled: !this.state.disabled });
this.props.onClick(this.props.el);
}
render() {
return (
<RaisedButton
disabled={this.state.disabled}
key={this.props.el}
label={this.props.el}
style={style.button}
onClick={() => this.handleClick()}
/>
);
}
}
export default OneButton;
In your resultsEngine function in the Results component you are specifying that you always want the _.last(selectedButtons) to be used. This is what it is doing, hence you always see the last button clicked. What you actually want is the index of that iteration to show.
const lastSelectedButton = selectedButtons[this.props.index];
const passwordString = passwordArray[selectedButtons[this.props.index]];
To get an index you have to create and pass one in, so create it when you map over the selected Buttons in the handleResults function in your Input component.
handleResults() {
return this.state.selectedButtons.map((el, index) => (
<Results key={el} appState={this.state} index={index} />
));
}

Categories