I am a beginner in Reactjs. I am trying to implement the Autocomplete component provided by material-ui. I want to pass the API link as a prop to the element. But how to pass the json label name as a prop to be used in "getOptionLabel"? For example, If we consider this API link which returns TV Show names, we need to use SHOW.NAME to access the name of the show.
getOptionLabel={(option) => option.show.name}
Here, the dynamic part is 'show.name'. How to pass this as prop? I tried doing
const label = 'show.name'
and then
getOptionLabel={(option) => option.label}
But his wouldn't work.
You need to pass the props in the function.
You could do something like this:
export default function App() {
const someData = [{
name: "abc"
}]
return ( <
Autocomplete myOptions={someData} />
);
}
export default function ComboBox(props) {
return ( <
Autocomplete id = "combo-box-demo"
options = {
props.myOptions
}
getOptionLabel = {
(option) => option.name
}
style = {
{
width: 300
}
}
renderInput = {
(params) => < TextField { ...params
}
label = "Combo box"
variant = "outlined" / >
}
/>
);
}
See it live here
Related
I'm creating a select (at the moment i'm using React-Select component) to retrive all the result from the api.
The problem is that API gives me back 20 values, so I should find a method to load other 20 values ( as I make another api call )
const option = personList && personList .map((spl) => {
return {
value: spl.perCod,
label: spl.perName
}
})
<Row>
<Col>
<Select
id="perCod"
name="perCod"
options={option}
/>
</Col>
</Row>
the personList is populated calling the api:
useEffect(() => {
sortEntities();
}, [paginationState.activePage, paginationState.order, paginationState.sort]);
const sortEntities = = () => {
//...
props.getFilteredEntities(
search, // i pass there the parameters for the research
paginationState.activePage - 1,
paginationState.itemsPerPage,
`${paginationState.sort},${paginationState.order}`
),
}
props.getFilteredEntities in my reducer is:
export const getFilteredEntities: ICrudSearchAction<Person> = (search, page, size, sort) => {
const params = new URLSearchParams(search) ? new URLSearchParams(search).toString() : null;
const requestUrl = `${apiUrl}${sort ? `?page=${page}&size=${size}&sort=${sort}` : ''}${sort ? '&' : '?'}${params}`;
return {
type: ACTION_TYPES.FETCH_PERSON_LIST,
payload: axios.get<Person>(`${requestUrl}${sort ? '&' : '?'}cacheBuster=${new Date().getTime()}`),
};
};
At the moment my select has the first 20 results from api. I should need to load others. How can I do? thank you.
change your <Select> code with this,
you have to add option tag within iteration, to render all options within select tag,
<Select id="perCod" name="perCod">
{option.map(o=><option key={Math.random()} value={o.perCod} >{o.perName}</option>)}
</Select>
Iam using multiple inputs inside maps i want to set focus to next input when i click enter in react Hooks.
With the help of refs
Iam using material ui text field for getting input
I tried in react class component wihtout ref it works with error but in hooks it not works
class compomnent code:
constructor(props) {
this.state = {}
}
inputRefs = [];
_handleKeyPress = e => {
const {currentTarget} = e;
let inputindex = this.inputRefs.indexOf(currentTarget)
if (inputindex < this.inputRefs.length - 1) {
this.inputRefs[inputindex + 1].focus()
}
else {
this.inputRefs[0].focus()
}
};
Inside render in added this within map function
this.state.data.map((data) => return (
<TextField
inputProps = {{onKeyPress:(e) => this.function1(e, data)}}
onChange={this.changevaluefunction}
inputRef={ref => this.inputRefs.push(ref)}
onFocus={this.handleFocus} ref={`input${id}`} /> ))
I have implemented the solution in a different way with the functional component. I have taken the 4 fields and seated its ref with the createRef hook.
I can see from your solution, you wanted to move focus to the next input element whenever you press Enter key on the current element.
I am passing the next target element argument in the onKeyUp handler along with the actual event and then detecting whether the Enter key is pressed or not. If Enter key is pressed and the targetElem is present then I am moving focus to the passed targetElem. By this way you have better control over the inputs.
You can see my solution here
https://codesandbox.io/s/friendly-leftpad-2nx91?file=/src/App.js
import React, { useRef } from "react";
import TextField from "#material-ui/core/TextField";
import "./styles.css";
const inputs = [
{
id: "fName",
label: "First Name"
},
{
id: "lName",
label: "Last Name"
},
{
id: "gender",
label: "Gender"
},
{
id: "address",
label: "Address"
}
];
export default function App() {
const myRefs = useRef([]);
const handleKeyUp = (e, targetElem) => {
if (e.key === "Enter" && targetElem) {
targetElem.focus();
}
};
return (
<div>
{inputs.map((ipt, i) => (
<TextField
onKeyUp={(e) =>
handleKeyUp(e, myRefs.current[i === inputs.length - 1 ? 0 : i + 1])
}
inputRef={(el) => (myRefs.current[i] = el)}
id={ipt.id}
fullWidth
style={{ marginBottom: 20 }}
label={ipt.label}
variant="outlined"
key={ipt.id}
/>
))}
</div>
);
}
You can convert this.inputRefs into a React ref so it persists through renders, and other than this you pretty much remove all references to any this object.
Example Component:
const LENGTH = 10;
const clamp = (min, max, val) => Math.max(min, Math.min(val, max));
export default function App() {
const [data] = useState([...Array(LENGTH).keys()]);
const inputRefs = useRef([]); // <-- ref to hold input refs
const handleKeyPress = index => () => { // <-- enclose in scope
const nextIndex = clamp(0, data.length - 1, index + 1); // <-- get next index
inputRefs.current[nextIndex].focus(); // <-- get ref and focus
};
return (
<div className="App">
{data.map((data, index) => (
<div key={index}>
<TextField
inputProps={{ onKeyPress: handleKeyPress(index) }} // <-- pass index
inputRef={(ref) => (inputRefs.current[index] = ref)} // <-- save input ref
/>
</div>
))}
</div>
);
}
If you are mapping the input field and want to focus on click, you can directly give the id attribute to the input and pass the array id.
After that, you can pass id inside a function as a parameter, and get it by document.getElementById(id).focus().
I have this component
const Pubs = () => {
const selectArea = useRef<HTMLInputElement | null>(null);
return (
<LikesComments selectArea={selectArea} publication={publication} />
<DisplayComments selectArea={selectArea} publication={publication} />
</PubHero>
);
};
export default Pubs;
And, when i pass the useRef to likesComments, i have this function who will use the useRef as props
interface IlikesCommentsProps {
selectArea: Idontknow
}
const LikesComments: React.FC<IlikesCommentsProps> = ({ selectArea }) => {
const focusComment = () => {
selectArea.current.focus()
}
return (
<div onClick={focusComment}>
<CommentIcon />
<LikeCommentText separation='0.7rem'>Comentar</LikeCommentText>
</div>
)
}
export default LikesComments
And when i click that function in the first comment, i want it to focus an input (textarea) in the second component
interface IcreateComments {
selectArea: Idontknow
}
const CreateComments: React.FC<IcreateComments> = ({ selectArea }) => {
return <textarea ref={selectArea} />
}
export default CreateComments
How can i make this happen?
I really haven't tried before to pass useRef as props, so, i really don't know what to do exactly, i've tried to look for answers, but found nothing at all
Can you help me with this?
Thanks for your time!
There are many ways to do it. I am showing one.
Firstly you can use a single ref for that. Just you have to take help of an menthod. The method will focus to the textarea.
const handleSelectArea = () => {
selectArea.current.focus()
}
Now pass them to the two component that you want
<LikesComments handleSelectArea={handleSelectArea} publication={publication} />
Inside the component onCLick call the method
const focusComment = () => {
handleSelectArea();
};
As you are already passing the ref to the text area so it will focus that.
You have to use the ref as bellow
type TextareaProps = React.HTMLProps<HTMLTextAreaElement>
const CreateComments = React.forwardRef<HTMLTextAreaElement, TextareaProps>((props, ref) => {
return <textarea ref={ref}/>
})
Simple Demo
I have a function that takes a Component as its' parameter. The function enables users to render their own popups instead of the ones I provide. However, I'm not able to add some props to said component before adding it to an array.
const addCustomSnack = (Snack, position) => {
let id = generate();
let snackProps = {
key: id,
id,
};
Snack.props = {...Snack.props, ...snackProps}
console.log(Snack);
buildStyle(position);
if (messagesNew.length >= 3) {
que.push(Snack);
addSnacks(messagesNew);
} else {
messagesNew = [...messagesNew, Snack];
addSnacks(messagesNew);
}
console.log(messagesNew);
};
This is what happens
Cannot assign to read only property 'props' of object '#<Object>'
I have tried the following code
const addCustomSnack = (Snack, position) => {
let id = generate();
console.log(Snack);
buildStyle(position);
if (messagesNew.length >= 3) {
que.push(Snack);
addSnacks(messagesNew);
} else {
messagesNew = [...messagesNew, <Snack key={id} id={id} />];
addSnacks(messagesNew);
}
console.log(messagesNew);
};
However, it will result in a React.createElement type error.
Codesandbox
Is there any way for me to add those props into the Snack component successfully?
This is exactly what a react High Order Component does: adding props to the component passed as parameter and return a component back.
If you are getting component in Snack then try below way
return <Snack {...snackProps} />
Using above code this will render any component that is passed to addCustomSnack
You could somehow keep an array of the component to render, each with a ref to the component and and its custom properties, then render it with a map, like so:
// Snack list
constructor(){
this.state = { snacks: [] }
}
// ...
const Lollipop = props => (
<div>
<h1>Lollipop</h1>
<span>Taste: </span> {props.taste}
</div>
)
const ChocolateBar = props => (
<div>
<h1>Chocolate bar</h1>
<span>With fudge: </span> {props.hasFudge ? 'yes': 'no'}
</div>
)
// Push a custom snack in the list
const addCustomSnack = (SnackType, props) => this.state.snacks.push({SnackType, props})
// ...
addSnack(Lollipop, {taste: 'cherry'})
addSnack(Lollipop, {taste: 'cola'})
addSnack(ChocolateBar, {hasFudge: true})
addSnack(ChocolateBar, {})
// render the lsit
const SnackList = () => {
<div>
{ this.state.snacks.map(({SnackType, props}, i) => (
<SnackType {...props} key={i} />
))}
</div>
}
React.cloneElement did exactly what I was looking for. Now, the user can give his own Component, and with cloneElement, I can extend the components props and add it into the array without problems.
const addCustomSnack = (Component, position) => {
let id = generate();
let props = {
removeSnack,
key: id,
id,
index: id
}
let Snack = React.cloneElement(Component, { ...props }, null);
buildStyle(position);
console.log(Snack)
if (messagesNew.length >= 3) {
que.push(Snack);
return addSnacks(messagesNew);
} else {
messagesNew = [...messagesNew, Snack];
return addSnacks(messagesNew);
}
};
Here is the render function
render(){
const { members, user } = this.props;
let memberOptions = [];
members.forEach((member, i) => {
memberOptions.push({
key: i,
text: member.user.name,
value: member.user.id,
image: { avatar: true, src: member.user.gravatar },
});
});
return (
<Dropdown placeholder='Select User' fluid selection options={memberOptions} />
)
}
This will render perfectly. But I also want to add one more text(say email) in this text field in a new line. So that dropdown items show similar to this : https://react.semantic-ui.com/elements/list#list-example-relaxed
How can I achieve this?
Akhila, I would recommend that you use the content prop instead of the text prop for your Dropdown.Item that you are rendering from your memberOptions array. The text prop specifically expects a string. The content prop will accept anything, including other React components or nodes. So instead of returning text as a string, you could do something like this for content, maybe as a separate class method on your component:
const renderItemContent = (member) => {
const {
email,
name,
} = member.user;
const emailStyle = {
color : '#333',
fontSize : '.875em',
}
return(
<React.Fragment>
{name}
{email &&
<div style={emailStyle}>{email}</div>
}
</React.Fragment>
)
}
Then set content: this.renderItemContent(member) on your memberOptionsArray.