React pass props from multiple arrays in state - javascript

I am trying to pass props from two arrays that are contained in the state of the react component. One array is generated by the user and the other is already built in. This is a bit over my head as I'm still new to React and am unsure how to correctly pass props.
There are no errors here in the component, it's just that I don't know how to do it.
I will explain below what I'm looking for, any help would be greatly appreciated
Main Component
In this.state below you will see questions (which works perfectly) then hints. Questions is mapped over correctly however when I try to add in hints to map along with it, it returns all of the hints at once instead of in order and one by one. I've tried just adding (questions, hints) but it doesn't return it correctly.
export default class AutoFocusText extends Component {
constructor() {
super();
this.state = {
active: 0,
questions: [
'1. Enter A Proper Noun',
'2. Enter A Location',
'3. Enter A Proper Noun that Describes Evil',
'4. Describe Something Menacing',
'5. Describe a fortified area',
"6. A Woman's Name",
'7. Describe a large area of mass'
],
hints: [
"Hint: Use words like Rebel, Hell's Angels",
'Hint: Use a word such as Base, Bunker, Foxhole, Bedroom',
'Hint: Use words like Empire, Ottoman, Mongols',
'Hint: Freeze Ray, Leftover Fruitcake',
'Hint: Castle, Bunker, Planet',
'Hint: Astrid, Diana, Mononoke, Peach',
'Hint: Use words such as Galaxy, Planet, Wal Mart'
],
answers: []
};
I'd like to take the user's inputs of answers and have it passed along as props into another component such as properName1={this.state.value1} and so on, I know that's mapping an array of answers, I'm just unsure how to do this.
Below is the rest of the main component.
this.submitHandler = this.submitHandler.bind(this);
this.renderQuestion = this.renderQuestion.bind(this);
this.onChange = this.onChange.bind(this);
}
renderQuestion() {
const { questions, hints, active, value } = this.state;
if (active >= questions.length)
return <Crawler style={{ width: '500px', position: 'absolute' }} />;
return questions.filter((quest, index) => index === active).map(quest => ( // get next question // map over selected question, the key prop allows react to
<FormElement
key={active}
text={quest}
hint={hints}
value={value}
onChange={this.onChange}
/>
));
}
onChange(e) {
this.setState({ value: e.target.value });
}
submitHandler(e) {
e.preventDefault();
const answers = [...this.state.answers, this.state.value]; //push new value to answsers array without mutation
const value = ''; // clear input
const active = this.state.active + 1; // index pointer
this.setState({ answers, value, active });
}
render() {
return (
<MainContainer>
<DivStyle>
{/* Form Wrapper */}
<form onSubmit={this.submitHandler}>
{this.renderQuestion()}
<SubmitButton type="submit">Submit</SubmitButton>
</form>
<ul>
{this.state.answers.map((ans, index) => {
return (
<li key={index}>
{ans}
</li>
);
})}
</ul>
</DivStyle>
</MainContainer>
);
}
}
Child Component 1
This is the dumb component where the questions (and where I want the hints as well) generated
class FormElement extends Component {
constructor() {
super();
}
componentDidMount() {
//focus text input upon mounting component
this.textInput.focus();
}
render() {
const { text, hint, value, onChange } = this.props;
return (
<div>
<InputQuestion>
{text}
{hint}
</InputQuestion>
<input
className="inputstyling"
ref={el => {
this.textInput = el;
}}
onChange={onChange}
type="text"
value={value}
/>
</div>
);
}
}
export default FormElement;
At the moment, "hint" brings in all the hints at once, instead of one at a time and in order.
Child Component 2
Finally the props needed to pass go here. The array is throwing me as I've never passed props via an array
class Crawler extends Component {
constructor() {
super();
this.state = {
properName1: 'Rebel',
noun1: 'frog',
properName2: 'Empire',
properName3: 'DEATH STAR',
noun2: 'station',
noun3: 'planet',
personsName1: 'Leia',
noun4: 'starship',
pluralnoun1: 'people',
noun5: 'galaxy'
};
}
render() {
return (
<ContainLeft style={{ padding: 0 }}>
<CrawlHolder>
<div class="fade" />
<section className="star-wars">
<div className="crawl">
<div className="title">
<p>Episode IV</p>
<h1>A New Hope</h1>
</div>
<p>
It is a period of civil war.
{' '}
{this.props.properName1}
{' '}
spaceships, striking from a hidden
{' '}
{this.props.noun1}
, have won their first victory against the evil Galactic
{' '}
{this.props.properName2}
.
</p>
<p>
During the battle,
{' '}
{this.props.properName1}
{' '}
spies managed to steal secret plans to the
{' '}
{this.props.properName2}
's ultimate weapon, the
{' '}
{this.props.properName3}
, an armored
{' '}
{this.props.noun2}
{' '}
with enough power to destroy an entire planet.
</p>
<p>
Pursued by the Empire’s sinister agents, Princess
{' '}
{this.props.personsName1}
{' '}
races home aboard her starship, custodian of the stolen plans that can save her people and restore freedom to the
{' '}
{this.props.noun3}
…
</p>
</div>
</section>
</CrawlHolder>
</ContainLeft>
);
}
}
export default Crawler;
Thank you for your help

the function in map has an optional second param, the index of the current element so you could do:
return questions.filter((quest, index) => index === active).map((quest,i) => (
<FormElement
key={active}
text={quest}
hint={hints[i]}
value={value}
onChange={this.onChange}
/>
));
EDIT:
I see now you are rendering only one question at a time so a map isn't needed I think; since you have the index of the current question (active) I think you could just
return (
<FormElement
text={questions[active]}
hint={hints[active]}
value={value}
onChange={this.onChange}
/>
)

Related

How to .Map over different props that are passed into a component?

I'm new to React but hopefully someone can help!
So I've just created a component that takes in a value (via prop) and then .maps over that value creating an Image slider. The props are all an array of objects that contain different values such as :
const Songs = [
{
artist: 'Artist Name',
song: 'Song Name',
lenght: '2:36',
poster: 'images/....jpg'
},
{
artist: 'Artist Name',
song: 'Song Name',
lenght: '2:36',
poster: 'images/....jpg'
},
]
I have been making the same component over and over again because I don't know how to make the 'prop'.map value dynamic. Essentially I don't know how to change the value before the .map each different prop.
Here's an example. I want to make 'Songs'.map dynamic so the new props can replace that so they can also be mapped. Maybe there's another way. Hopefully some can help.
import React from 'react';
import { FaCaretDown } from 'react-icons/fa';
function ImageSlider({Songs, KidsMovies, Movies, TvShows}) {
return (
<>
{Songs.map((image, index) => (
<div className="movie-card">
<img src={'https://image.tmdb.org/t/p/w500' + image.poster_path}
className='movie-img' />
<h5 className='movie-card-desc'>{image.original_title}</h5>
<p className='movie-card-overview'>{movie.overview}</p>
</div>
))}
</>
);
}
export default ImageSlider;
Given your example,
I feel like all you need is render ImageSlides for each array
function ImageSlider({ items }) {
return (
<>
{items.map((item, idx) => (
<div ... key={idx}> // be careful to not forget to put a key when you map components
...
</div>
))}
</>
);
}
When rendering your component
function OtherComponent({ songs, kidsMovies, movies, tvShows }) {
return (
<div>
<ImageSlider items={songs} />
<ImageSlider items={kidsMovies} />
<ImageSlider items={movies} />
<ImageSlider items={tvShows} />
</div>
);
}

How to generate a component in React Form onClick

I am struggling with some React functionality. My goal is to create a form where a day template can be added (for context - like a training club can make up a template of trainings for the day and then schedule them regularly). For that I wanted to add a button which onClick will create a smaller block with 2 form fields - time and training info. And I need user to add several of those, as much as they want.
The thing is, while I understand a bit how react works, it seems to me I am just banging my head against the wall with this, as one thing is to render a component, but another to generate a bunch of same, completely new ones and connected to the form somehow, so I can send the data when clicking submit button.
Here is repository with this component:
https://github.com/badgerwannabe/spacefitness-test-client
Here is path to this component
spacefitness-test-client/src/components/template-components/addTemplateForm.js
Here below how it looks rendered
UPDATE 1 here is the full component here:
import React, {useState} from "react";
import {useMutation } from "#apollo/client";
import {useForm} from '../../utils/hooks'
import { Button, Form } from "semantic-ui-react";
import {FETCH_TEMPLATES_QUERY, FETCH_TRAININGS_QUERY,ADD_TEMPLATES_MUTATION} from '../../utils/graphql'
//hook for form functioning
function AddTemplateForm (props){
const {values, onChange, onSubmit} = useForm(createDayCallback,{
date:'', dayTrainings:[{
time:'testing time', training:"60e9e7580a6b113b2486113a"
},{
time:'testing2 time2', training:"61ec6a6d0f94870016f419bd"
}
]
});
//apollo hook to send data through GraphQL
const [createDay, {error}] = useMutation(ADD_TEMPLATES_MUTATION, {
errorPolicy: 'all',
variables:values,
update(proxy, result){
const data = proxy.readQuery({
query:FETCH_TEMPLATES_QUERY,
});
proxy.writeQuery({query:FETCH_TEMPLATES_QUERY,
data:{
getDays: [result.data.createDay, ...data.getDays]
}})
props.history.push('/templates')
},},
{});
function createDayCallback(){
createDay();
}
//little component I want to dynamically add each time people press a button
function addDayTraining(){
const addDayTraining = (
<>
<Form.Field>
<Form.Input
placeholder="time"
name="time"
onChange={()=>{
console.log("time")
}}
values={values.time}
error={error ? true : false}
/>
<Form.Input
placeholder="training"
name="training"
onChange={()=>{
console.log("training")
}}
values={values.training}
error={error ? true : false}
/>
</Form.Field>
</>
)
return addDayTraining
}
//Form component itself
const AddTemplateForm = (
<>
<Form onSubmit={onSubmit}>
<h2>Add a template :</h2>
<Form.Field>
<Form.Input
placeholder="date"
name="date"
onChange={onChange}
values={values.date}
error={error ? true : false}
/>
</Form.Field>
<Form.Field>
<Button type="button" onClick={
addDayTraining
}>Add training</Button>
</Form.Field>
<Button type ="submit" color="teal">Submit</Button>
</Form>
{error && (
<div className="ui error message" style={{marginBottom:20}}>
<li>{error.graphQLErrors[0].message}</li>
</div>
)}
</>
)
return AddTemplateForm
}
export default AddTemplateForm;
Can you just set up a function on the submit button which pushes an object with {time: new Date(), trainingInfo: ""} and push that object into an existing array of training objects? (obviously starting empty)
You could then map those objects into a component and when the component is updated (i.e. when the user adds a time and training details text) use a callback function to update the values in the array at the index of that object.
export default function yourIndexPage({yourprops}) {
const [trainingObjects, setTrainingObjects] = useState([]);
function addTraining(){
const newTrainingObject = {
time: new Date(), //assuming you want it to default to todays date
trainingInfo: "your placeholder text"
};
setTrainingObjects([...trainingObjects, newTrainingObject]);
}
//I am assuming your training object will be a list item here so wrapped in <ul>
return(
<div>
<div className='your list of training things'> (might need to set style as flex and add some padding etc..)
{trainingObjects.length === 0 ? <div/> : trainingObjects.map((trainingObject, index) => (
<YourTrainingObjectComponent trainingObject={trainingObject} trainingItemIndex={index} key={index}/>
))}
</div>
<Button onClick={() => {addTraining}} />
</div>
)
}

React component failed to render without any error or warning

I am writing a React component like this. My goal is to render a checklist for shopping, with toggles (lower order component) that would update the state of this higher order component when clicked.
It may look something like this
1.Fruits
Banana (Button)
Kiwi Fruit (Button)
2.Other snacks
Potato chips (Button)
Instant noodles (Button)
Sugarfree mint (Button)
let shopping_list = [
{
Fruits: [
{ Banana: "banana" },
{ "Kiwi fruit": "kiwi" },
]
},
{
'Other snacks': [
{ "Potato chips": "potato_chips" },
{ "Instant noodles ": "instant_noodles" },
{ "Sugarfree mint": "sugar_free_mint" }]
}
]
class ShoppingList extends Component {
constructor(props) {
super(props);
this.btnClicked.bind(this);
this.state = {
banana: false,
kiwi: false,
potato_chips: false,
instant_noodles: false,
sugar_free_mint: false,
}
}
btnClicked = (e) => {
let obj = {};
obj[e.currentTarget.id] = !(this.state[e.currentTarget.id]);
this.setState(obj);
}
createToggle = (content, attribute) => {
return (
<Row key={attribute}>
<Col>
{content}
</Col>
<Col xs="2">
<Chips label="✓"
id={attribute} onClick={this.btnClicked} />
</Col>
</Row>
)
}
createNestedToggle = (shopping_list) => {
//console.log("creating nested toggle")
//console.log(this)
shopping_list.map( (section, index) =>
( <React.Fragment key={index}>
<Row className="table_text" style={{paddingTop: 5}}>
<Col xs="12" style={{fontWeight: "bold"}}>
{index+1}.{(Object.keys(section))[0]}
</Col>
</Row>
{ (Object.values(section))[0].map((item) => {
this.createToggle(
(Object.keys(item))[0],
(Object.values(item))[0]
)
}
)
}
</React.Fragment>
))
}
render() {
return (
<div className="animated fadeIn">
<Title type="title" content="Shopping list" />
...
{/**Some rows and columns in here */}
<div>
{this.createNestedToggle(shopping_list)}
</div>
<div>
{/**Some rows and columns in here */}
</div>
</div>)
}
}
But the shopping list wasn't rendered properly, it was missing. When I was debugging, I tried adding console log messages to the function createNestedToggle as you can see, and they're actually logged. I am not sure what exactly is wrong with the second block.
Things I've tried
I have tried writing the createToggle part into a functional component to simplify the code - however,
as I need the toggles to call back the HOC I have to make createToggle a part of ShoppingList class.
I am quite new to React JS(JS to be exact), so maybe I am doing the whole thing in a way that's
completely wrong. If you could suggest me a better way to do it, I would be very grateful as well.
Any help or hint would be appreciated, thank you :)
After I have modified the createNestedToggle function as the following and it worked
createNestedToggle = (part_iii) => {
return(
<div>
{part_iii.map( (section, index) =>
<div key={index}>
...
{
(Object.values(section))[0].map((item) => {
return(
this.createToggle(
(Object.keys(item))[0],(Object.values(item))[0] )
)
}
)
}
</div>
)}
</div>
)
}
I was always confused by the arrow function notation and didn't realise that it has to be ()=>{...return ...} . (Yep I should read the docs more carefully) I will leave this post here in case any future Googlers need it. Thanks for everyone's answer.

react tips not appearing?

I am working on a component right now that is a mapped stack of divs. Each one should have a tooltip but for the life of me I can't get the tooltip to appear
class App extends Component {
constructor() {
super();
this.state = {
options: [
{
id: '1',
label: 'Industrial Truck and Tractor Operators',
value: '53-7051',
tooltip_text: 'Operate industrial trucks or tractors equipped to move materials around a warehouse, storage yard, factory, construction site, or similar location. Excludes “Logging Equipment Operators" (45-4022).',
},
{
id: '2',
label: 'Order Clerks',
value: '43-4151',
tooltip_text: 'Receive and process incoming orders for materials, merchandise, classified ads, or services such as repairs, installations, or rental of facilities. Generally receives orders via mail, phone, fax, or other electronic means. Duties include informing customers of receipt, prices, shipping dates, and delays; preparing contracts; and handling complaints. Excludes "Dispatchers, Except Police, Fire, and Ambulance" (43-5032) who both dispatch and take orders for services.',
},
],
value: null,
className: '',
selectedClassName: '',
loading_State: true, loads
childrenCount: 0
};
this.setProps = this.setProps.bind(this);
}
setProps(newProps) { //this is going to update the state
this.setState(newProps);
}
render() {
return (
<div>
<DashControlledContainer
setProps={this.setProps}
options = {this.state.options}
value = {this.state.value}
styles = {this.state.styles}
className = {this.state.className}
selectedClassName = {this.state.selectedClassName}
loading_State = {this.state.loading_State}
childrenCount = {this.state.childrenCount}
/>
</div>
)
}
}
export default App;
//the component being returned with the tooltip
render(){
return (
<div style={this.props.styles}>
{this.props.options.map(option => (
<div key = {option} id={option.id} style={option.style}
onClick = {e=>{ //updates the props with the clicked targets value if setProps is accessible
if(this.props.setProps){
this.props.setProps({value: e.target.value})
}else{
this.setState({value:e.target.value})
}
}}
>
<span id={option.id}> {option.label} </span>
<UncontrolledTooltip placement="right" target={option}>
{option.tooltip_text}
</UncontrolledTooltip>
</div>
))}
</div>
);
}
I'm not sure where to set my target for the tooltip maybe thats the issue? I haven't been able to find many resources online. Any help would be appreciated.
I think you should provide id as a target to your UncontrolledTooltip,
<UncontrolledTooltip placement="right" target={option.id}> //set id of span as a target here
hello
</UncontrolledTooltip>
I was able to figure it out, including a string in my id fixed it for some reason?
i made my div contain id={"option"+option.id}
then I referenced it like so:
<UncontrolledTooltip placement="right" target={"option"+option.id}>
{option.tooltip_text}
</UncontrolledTooltip>

Reactjs style component with document.getElementById

Hello i'm trying to highlight the Items, with the same Id. Im using document.getElementById but i don't really know ho to do it. does anyone can help me?
I'm iterating over an Array of Objects from my Database....
return(
<div className='list'>
<Row>
{this.state.cards.map((card) => {
return(<Card
onHover={()=>{
const highlighted = document.getElementById(card.id)
highlighted.style={{backgroundColor: "blue"}}
}}
cardHeading={card.cardHeading} cardDesc={card.cardDesc}
cardPreis={card.cardPreis} cardId={card.id} bewertung={card.cardBewertung}
image={card.cardImage}/>)
})
}
</Row>
</div>
)
.... My GoogleMaps Component:
<Marker onClick={props.show}
position={{ lat: marker.latitude + 0.00201, lng:
marker.longitude + 0.00201}}
id={marker.id}
/>
id={marker.id} and cardId={card.id} are the same and i want to highlight one of them when hovering over them ... thanks in advance.
React gives you the ability for a component to control itself dynamically. So make that Card into a separate component with all of the logic you need using this.setState to control the current style of your Card. I can't test this, but this is the general idea:
return(
<div className='list'>
<Row>
{this.state.cards.map((card) => <CustomCard card={card}/>)}
</Row>
</div>
)
class CustomCard extends Component {
constructor() {
super()
this.state = {
defaultColor: true
}
}
handleClickColorChange() {
this.setState({defaultColor: !this.state.defaultColor})
}
render() {
const {card} = this.props
const customStyle = {
backgroundColor: 'blue'
}
return (
<Card
onHover={() => this.handleClickColorChange()}
style={this.state.defaultColor ? null : customStyle}
cardHeading={card.cardHeading} cardDesc={card.cardDesc}
cardPreis={card.cardPreis} cardId={card.id} bewertung={card.cardBewertung}
image={card.cardImage}
/>
)
}
}

Categories

Resources