There is array with words. const = ['cat','dog']. Also textarea or input, or editable div. I need to write words in this field and highlight words if they match.
Now I have a bad variant because I use the highlight npm package and contain it over the input field and hide input text. Caret runs ahead and also I have a lot of problems besides all this.
The default value from this form goes to useState.
Also, I should be able to call the onKeyPress function or something similar, because I add a new tag if e.keyCode === 32 and contain #.
I need to do it use react function component.
Are you looking for something like this(code mentioned below)?
Also, I didn't understand the part of using a key, what do you exactly want to achieve with it.
And keyCode is deprecated, I'll suggest you use key key==='#'
Plus, if you can add a code snippet or elaborate more would help in helping you better
import { useState, useEffect, useMemo } from "react";
const Input = () => {
const [inputValue, setInputValue] = useState("");
const tagsArr = useMemo(() => ({ cats: false, dogs: false }), []);
useEffect(() => {
Object.keys(tagsArr).forEach((el, i) => {
if (inputValue.includes(el)) {
tagsArr[el] = true;
} else {
tagsArr[el] = false;
}
});
}, [inputValue, tagsArr]);
const handleChange = ({ target }) => {
setInputValue(target.value);
};
return (
<div>
<input type="text" value={inputValue} onChange={handleChange} />
<div>
<ul>
{Object.keys(tagsArr).map((el, i) =>
tagsArr[el] ? (
<li
key={el}
style={{
listStyle: "none",
backgroundColor: "springgreen",
width: "max-content",
display: "inline",
padding: "10px",
marginRight: "10px",
}}
>
{"#" + el}
<button>X</button>
</li>
) : null
)}
</ul>
</div>
</div>
);
};
export default Input;
Related
I'm trying to display fields based on the value of a props so let's say my props value = 2 then I want to display 2 inputs but I can't manage to get it work.
This is what I tried
const [numberOfFields, setNumberOfFields] = useState(0);
const [loadFields, setloadFields] = useState([]);
const addField = () => {
return loadFields.map((tier) => {
<div>
<p style={{color:'black'}}>Tier {tier + 1}</p>
<InputNumber />
</div>
})
}
const onPropsValueLoaded = (value) => {
let tmp = value
setNumberOfFields(tmp);
if (numberOfFields > 0) {
const generateArrays = Array.from(value).keys()
setloadFields(generateArrays);
} else {
setloadFields([]);
}
}
useEffect(() => {
onPropsValueLoaded(props.numberOfTiers);
}, [])
return (
<>
<Button type="primary" onClick={showModal}>
Buy tickets
</Button>
<Modal
title="Buy ticket"
visible={visible}
onOk={handleOk}
confirmLoading={confirmLoading}
onCancel={handleCancel}
>
<p style={{ color: 'black' }}>{props.numberOfTiers}</p>
{loadFields.length ? (
<div>{addField()}</div>
) : null}
<p style={{ color: 'black' }}>Total price: </p>
</Modal>
</>
);
so here props.NumberOfTiers = 2 so I want 2 input fields to be displayed but right now none are displayed even though loadFields.length is not null
I am displaying this inside a modal (even though I don't think it changes anything).
I am doing this when I load the page that's why I am using the useEffect(), because if I use a field and update this onChange it works nicely.
EDIT:
I changed the onPropsValueLoaded() function
const generateArrays = Array.from({length : tmp}, (v,k) => k)
instead of
const generateArrays = Array.from(value).keys()
There are couple of things you should fix in here,
First, you need to return div in addField function to render the inputs.
Second, you should move your function onPropsValueLoaded inside useEffect or use useCallback to prevent effect change on each render.
Third, your method of creating array using Array.from is not correct syntax which should be Array.from(Array(number).keys()).
So the working code should be , I also made a sample here
import React, { useState, useEffect } from "react";
import "./styles.css";
export default function App() {
const [numberOfFields, setNumberOfFields] = useState(0);
const [loadFields, setloadFields] = useState([]);
const addField = () => {
return loadFields.map((tier) => {
return (
<div key={tier}>
<p style={{ color: "black" }}>Tier {tier + 1}</p>
<input type="text" />
</div>
);
});
};
useEffect(() => {
let tmp = 2; // tier number
setNumberOfFields(tmp);
if (numberOfFields > 0) {
const generateArrays = Array.from(Array(tmp).keys());
setloadFields(generateArrays);
} else {
setloadFields([]);
}
}, [numberOfFields]);
return (
<>
<button type="button">Buy tickets</button>
<p style={{ color: "black" }}>2</p>
{loadFields.length ? <div>{addField()}</div> : null}
<p style={{ color: "black" }}>Total price: </p>
</>
);
}
I need to pass "notecards" (an array) down from "Notecard.js" to "LoadQuestions.js". Console log shows that it is passing, but when I use {notecards} within the "return" it errors as "undefined". Could you please take a look?
Notecard.js (without the imports):
const useStyles = makeStyles((theme) => ({
root: {
maxWidth: 345,
},
media: {
height: 0,
paddingTop: '56.25%', // 16:9
},
}));
export default function Notecard( {notecards} ) {
const classes = useStyles();
const next = () => {
console.log('Next Button Clicked')
};
const previous = () => {
console.log('Back Button Clicked')
};
const hint = () => {
console.log('Hint Button Clicked')
};
console.log({notecards});
return (
<Card className={classes.root}>
<div id="cardBody">
<CardHeader
title="Kate Trivia"
// subheader="Hint: In the 20th century"
/>
<CardContent>
<LoadQuestions notecards={notecards}/>
</CardContent>
</div>
</Card>
);
}
LoadQuestions.js (without imports)
const {useState} = React;
export default function LoadQuestions( {notecards} ) {
const [currentIndex, setCounter] = useState(0);
console.log({notecards});
return (
<div>
<Toggle
props={notecards}
render={({ on, toggle }) => (
<div onClick={toggle}>
{on ?
<h1>{props.notecards} hi</h1> :
<h1>{this.props[currentIndex].backSide}</h1>
}
</div>
)}
/>
<button onClick={() => {
console.log({notecards})
if (currentIndex < (this.props.length-1)) {
setCounter(currentIndex + 1);
} else {
alert('no more cards')
}
}}>Next Card
</button>
<button onClick={() => {
if (currentIndex > 0 ) {
setCounter(currentIndex -1);
} else {
alert('no previous cards')
}
}}>Previous Card
</button>
</div>
);
}
Thanks in advance!
That's all the details I have for you, but stack overflow really wants me to add more before it will submit. Sorry!
You should check if props exists, first time it renders the component it has no props so it shows undefined.
First i must say you destructured notecards out, so no need to use props.
If you want to use props you should change
({notecards}) to (props)
and if not you can directly use notecards since it is destructured
I suggest you two ways
adding question mark to check if exists
<h1>{props?.notecards} hi</h1>//in the case you want to use props
or
add the props in a if statement
<h1>{props.notecards?props.notecards:''} hi</h1> // if notecards is destructured remove the "props."
Please help me out.
I want to add dynamically extra input fileds and save the values into textValues state.
Here is what I tried until now:
const [textValues, setTextValues] = useState([]);
const [numberOfTexts, setNumberOfTexts] = useState(5); <--- this value will be change dynamically
{[...Array(numberOfTexts)].map((x, i) => (
<TextField
style={{ marginLeft: "10px", marginTop: "10px" }}
id="standard-basic"
label="Text value"
value={textValues[i]}
onChange={(e: { target: { value: any } }) =>
setTextValues(e.target.value)
}
/>
))}
So, in this case I want appear 5 input fields and in the textValues array to be 5 empty strings, than fill in step by step. I want to be able to increase and decrease the numberOfTexts, and update everything according to the number of texts. Thank you for your help!
For simple usecase you don't need to have 2 useState to handle the number of fields and their values.
Usually this is a best practice to define the minimal data set model.
You can write abstract function on top of setTextValues, it should do the trick ;)
import React, { useState } from "react";
import "./styles.css";
export default function App() {
const [textValues, setTextValues] = useState([]);
const addField = () => {
setTextValues(textValues.concat([""]));
};
const updateField = (index, newValue) => {
const newValues = textValues.map((val, idx) => {
if (idx === index) {
return newValue;
}
return val;
});
setTextValues(newValues);
};
const removeField = () => {
setTextValues(textValues.slice(0, textValues.length - 1));
};
return (
<div>
<button onClick={() => addField()}>Add field</button>
<button onClick={() => removeField()}>Remove (last) field</button>
<br />
<div>
{textValues.map((textValue, idx) => (
<input
style={{ marginLeft: "10px", marginTop: "10px" }}
id="standard-basic"
label="Text value"
value={textValue}
onChange={(e) => {
updateField(idx, e.target.value);
}}
/>
))}
</div>
</div>
);
}
To go further, you may want to create custom hook to abstract this logic, and have reusable chunk of state. The API may look like this:
const { addField, removeField, updateField } = useInputArray(['hello', 'yo'])
One way to do this in a way that doesn't require too many changes to your code so far is to use a useEffect() hook to update the string array as numberOfTexts changes.
You could do this by adding this code:
useEffect(() => {
let dif = numberOfTexts - textValues.length;
if (dif > 0) {
setTextValues(textValues.concat(Array(dif).fill('')));
} else if (dif < 0) {
setTextValues(textValues.slice(0, numberOfTexts));
}
}, [numberOfTexts]);
This will dynamically update the textValues array based on the numberOfTexts array.
So im working on a React app, it's basically a CRUD app pretty much (serving right now as a learning practice project).
Anyways, I have a modal that appears when the user needs to edit some basic information on a resource (right now it's just a "name" that's being edited). The modal is a presentational/dumb component that takes in props from it's parent (Which is essentially just a table of the resources/items).
If im literally only going to be submitting the form to change 1 field I would only need a state with 1 item (for that input field to edit).......does it make sense to STILL make it a class just because it has a state? From what i've read if you have a state you should automatically make it a class?
Is this always true?
Are you talking like this?
When you click on any list item it goes to child(functional) component as you were asking.
You can check working live example Live demo
export default function App() {
const [arr, setArr] = useState(["One", "Two", "Three"]);
const [name, setName] = useState("");
const handleChange = e => {
setName(e.target.value);
};
const handleSubmit = e => {
e.preventDefault();
setArr([...arr, name]);
setName("");
};
return (
<div>
{arr.map(a => (
<div style={myStyle} key={a}>
{a} <button onClick={() => setName(a)}>Edit</button>
</div>
))}
<Child
handleChange={handleChange}
handleSubmit={handleSubmit}
name={name}
/>
</div>
);
}
const Child = ({ handleChange, name, handleSubmit }) => {
return (
<form onSubmit={handleSubmit}>
<input type="text" value={name} onChange={handleChange} />
<input type="submit" value="Save" />
</form>
);
};
const myStyle = {
margin: 5,
cursor: "pointer",
border: "1px solid lightgrey",
padding: "5px",
display: "flex",
justifyContent: "space-between"
};
Yes whenever you use a state you should make it a class (way back then) but now we have this thing called HOOKS that allows you to use states within functional components.
Simple sample implementation below.
import React, { useState } from 'react';
const Example = () => {
//creates a state name and a setter for it. Then initialize it with empty string
const [name, setName] = useState("");
return (
<div>
//suppose you have input here
<button onClick={() => setName("Your name value")}>
Click me
</button>
</div>
);
}
UPDATED:
function TheCards() {
let cardMasterList = require('./scryfall-oracle-cards.json')
let cardMasterListSorted = sortByKey(cardMasterList, 'name')
let [cardClicked, setCardClicked] = useState(null)
//console.log(cardMasterList[0].name)
//console.log(cardMasterListSorted)
const handleClick = () => {
setCardClicked('red')
console.log(cardClicked)
//Please make this do something with the clicked span tag!
}
return (
<form id="card-master-list">
{cardMasterListSorted
.filter(({legalities}) => legalities.vintage === 'legal')
.filter(({rarity}) => rarity === 'common' || 'uncommon')
.slice(0,10)
.map(
(cardName) => {
return (
<li key={cardName.id}>
<span className="card-name" onClick={ handleClick }>{cardName.name}</span>
</li>
)
}
)
}
</form>
)
}
Update: This is the updated code above. I STILL can't figure out how to reference what I'm clicking on. I want to do more than simply change the color of the text, I'm just using "change it to red" as something simple to do. I am sad to say I'm missing jQuery and its easy "this" reference points, because something like "this.handleClick" doesn't work.
function TheCards() {
let cardMasterList = require('./scryfall-oracle-cards.json')
let cardMasterListSorted = sortByKey(cardMasterList, 'name')
console.log(cardMasterList[0].name);
console.log(cardMasterListSorted)
return (
<form id="card-master-list">
{cardMasterListSorted
.filter(({legalities}) => legalities.vintage === 'legal')
.filter(({rarity}) => rarity === 'common' || 'uncommon')
.map(
(cardName) => {
return (
<li key={cardName.id}>
<span className="card-name" onClick={ function DoTheThing() { document.querySelector('.card-name').style.color = 'red' } }>{cardName.name}</span>
</li>
)
}
)
}
</form>
)
}
All I want to do is so when the span is clicked on, it turns red. This seems impossible as nothing I try works - writing another function to fire in any way I can think of. "this" isn't working as expected. What can I replace the document.querySelector line with to make it work?
The method in your code is not the recommended way of updating the UI when rendering with React. Ideally you would update the style of the span via styling data stored in your components state:
<li key={cardName.id}>
<span className="card-name" onClick={
() => {
/* Set the color state of component to red which triggers a
render() and will update the style prop of the span
accordingly */
this.setState({ color : 'red' });
}
} style={{ color : this.state.color }}>
{cardName.name}</span>
</li>
If you really want to update the styling via the pattern in your original code, then one way to do this would be via a "callback ref" which would give you access to the span's corresponding DOM element:
<li key={cardName.id}>
<span className="card-name" ref={ (spanElement) => {
spanElement.addEventListener('click', () => {
spanElement.style.color = 'red';
});
}}>
{cardName.name}</span>
</li>
Here's an example of how to update styles the React way:
import React, { useState } from "react";
import ReactDOM from "react-dom";
const tiles = [
{
id: 1,
n: "one"
},
{
id: 2,
n: "two"
}
];
const App = () => {
const [clickedId, setClickedId] = useState(null);
const click = id => {
setClickedId(id);
};
return (
<div style={{ display: "flex", flexDirection: "column" }}>
{tiles.map(tile => (
<div
onClick={() => click(tile.id)}
style={{ color: tile.id === clickedId ? "red" : "black" }}
>
{tile.n}
</div>
))}
</div>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Live example here.
That should work as you have it. Though as codecubed.io pointed out is not a very react way to do it. instead it is preferred to toggle a class. something like this:
const styles = {
redTextClass: {
color: 'red',
}
}
<span className={stateVariable ? styles.redTextClass : null}></span>