I got my component who won't check the radio when i go to the /view/:id for the second time. I started in my list component with react-router at the index of the site, i click on the view button of an element, the radio is checked, i return in my list and go to another or the same element and it's not checked anymore. When i inspect the component in the React developer tool, the radio has the defaultChecked=true property.
import React from 'react';
import { connect } from 'react-redux';
class LicenseRadios extends React.Component {
buildRadios() {
let { licenses, activeValue } = this.props;
return licenses.map(license => {
let checked = false;
if(activeValue !== undefined && activeValue === license.id){
checked = true;
}
return (
<div key={license.id} className="col l2">
<p>
<input name="license" type="radio" id={'licenseRdo_' + license.id} value={license.id} defaultChecked={checked} />
<label htmlFor={'licenseRdo_' + license.id}>{license.label}</label>
</p>
</div>
);
});
}
render() {
return (
<div className="row">
{this.buildRadios()}
</div>
);
}
}
export default LicenseRadios;
I tried to change the defaultChecked for the checked attribute, but it require an onChange event. I don't understand this problem. Can anybody help me please?
Thank you
The defaultChecked prop is only used during initial render. If you need to update the value in a subsequent render, you will need to use an onChange function to handle value changes.
Check out controlled components in the docs to better understand the problem you're having.
use "undefined" for initial value for defaultChecked and re-render by setting it to true or false
const Example = () => {
[checked,setChecked] = useState(undefined);
useEffect(()=>{
// fetch data
setChecked(true);
});
return (
<input type="checkbox" defaultChecked={checked} onClick={(e)=> changeValue(e)}/>
);
}
Related
I'm using Vue 3, NaiveUI, Vitest + Vue Testing Library and got to the issue with testing component toggle on button click and conditional rendering.
Component TSample:
<template>
<n-button role="test" #click="show = !show" text size="large" type="primary">
<div data-testid="visible" v-if="show">visible</div>
<div data-testid="hidden" v-else>hidden</div>
</n-button>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue'
import { NButton } from 'naive-ui'
export default defineComponent({
name: 'TSample',
components: {
NButton
},
setup() {
const show = ref(true)
return {
show
}
}
})
</script>
The test case I have:
import { render, waitFor } from '#testing-library/vue'
import TSample from './TSample.vue'
import userEvent from '#testing-library/user-event'
describe('Tests TSample component', () => {
it('toggles between visible and hidden text inside the button', async () => {
const user = userEvent.setup()
const { getByText, queryByText, getByRole } = render(TSample)
expect(getByRole('test')).toBeInTheDocument()
expect(getByText(/visible/i)).toBeInTheDocument()
expect(queryByText('hidden')).not.toBeInTheDocument()
await user.click(getByRole('test'))
await waitFor(() => expect(queryByText(/hidden/i)).toBeInTheDocument()) <-- fails
})
})
The error:
<transition-stub />
<span
class="n-button__content"
>
<div
data-testid="visible"
>
visible
</div>
</span>
</button>
</div>
</body>
</html>...Error: expect(received).toBeInTheDocument()
received value must be an HTMLElement or an SVGElement.
Moreover in Testing Preview I get:
<button
class="n-button n-button--primary-type n-button--large-type mx-4"
tabindex="0"
type="button"
disabled="false"
role="test"
>
<transition-stub>
</transition-stub><span class="n-button__content">
<div data-testid="visible">visible</div>
</span>
</button>
a button, which makes it more confusing to me... Same situation happened when I replaced waitFor with nextTick from Vue, the component didn't do a toggle at all on click.
What works but isn't acceptable
When I changed the n-button to just button, the test passed and divs are toggled, but that's not the goal of this component. The component isn't supposed to be changed.
What I have tried:
I tried different approaches with reaching the div that contains hidden text. Either it was like above - queryByText/getByText or getByTestId, but test fails at the same point.
Also followed with similar approach shown at Testing Library - Disappearance Sample
but doesn't work in my case above.
What actually is going on and how can I test the change on click with such third-party components?
If more info is needed/something is missing, also let me know, I'll update the question.
Any suggestions, explanations - much appreciated.
I'm currently using react-select and I want to create a custom Option component that has an icon that allows interaction (e.g. adding to favourites). I've tried doing
const Option = props => {
return <components.Options {...props}>
<p>Text</p>
<IonIcon onClick={console.log("clicked")} />
</components.Options>
}
Doing this does not allow the IonIcon to be clicked and the onClick behaves like the default Option.
I have also tried:
<div>
<p>Text</p>
<IonIcon onClick={console.log("clicked")} />
</div>
as the return. While doing this, the default Option behaviour is removed, it does not allow the IonIcon to be clicked either.
For both methods I've tried, opening the menu will trigger the onClick, i.e. print "clicked", and clicking the IonIcon doesn't trigger anything.
I've set the zIndex of the icon to 10000 as well.
Thanks in advance! It'll be really cool to include the ability to favourite in select!
Before Clarifications
If I didn't understand wrong you want your new Component to have some kind of text (maybe passed by prop?) and the Icon clickable, after the click the Icon should change color.
For the Icon I'm using react-icons .
App.js
import React,{useState} from 'react';
import { IoStar } from "react-icons/io5";
function App() {
return (
<div style={{textAlign:"center"}}>
<ul style={{listStyleType:"none"}}>
<Option value="Location 1"/>
<Option value="Location 2"/>
<Option value="Location 3"/>
<Option value="Location 4"/>
<Option value="Location 5"/>
</ul>
</div>
);
}
const Option = (props) =>
{
let {value} = props; //Value taken from props
const defaultStyle = //Default Logo Style -> Cursor: Pointer is used to make the cursor become a "hand" when "hover" on the icon
{
cursor:"pointer",
fontSize:"40px"
}
const starPressed = () => //Function triggered on the "On Click"
{
if(isYellow)
setIconStyle(defaultStyle); //Color Default
else
setIconStyle({...iconStyle,color : "#f5d442"}); //Color Change
setIsYellow(!isYellow);
}
const [iconStyle,setIconStyle] = useState(defaultStyle); //UseState
const [isYellow,setIsYellow] = useState(false); //UseState
//return
return(
<li>
<span style={{fontSize:"40px"}}>{value}</span> <span><IoStar onClick={starPressed} style={iconStyle}/></span>
</li>
)
}
export default App;
Basically I'm using useState to re-render after the : iconStyle changes.
I believe it is what you were searching for.
EDIT After Clarifications
This is a BIG BIG work-around, hope it would help you.
I'm sure there are better ways to pass data and other things, and I'm still looking for them, but this is my first approach to it.
(Never used react-select).
Hope it helps.
import React,{useState} from 'react';
import { IoStar } from "react-icons/io5";
import Select from "react-select";
function App() {
const [isYellow,setIsYellow] = useState(
[
false,
false
]
); //UseState
const options =
[
{
value:0,
label:"Option 1",
isYellow: isYellow,
setIsYellow: setIsYellow
},
{
value:1,
label:"Option 2",
isYellow: isYellow,
setIsYellow: setIsYellow
}
];
return (
<>
<Select closeMenuOnSelect={false} options={options} formatOptionLabel={FormatOptionLabel}/>
</>
);
}
const FormatOptionLabel = ({ value, label,isYellow,setIsYellow}) =>
{
const style1 =
{
zIndex:"100",
cursor:"pointer",
fontSize:"30px"
}
const style2 =
{
zIndex:"100",
cursor:"pointer",
fontSize:"30px",
color : "#f5d442"
}
//return
return(
<>
<span style={{fontSize:"20px"}} value={value}>{label}</span> <span style={{float:"right"}}><IoStar onClick={(event) => { let array = isYellow; array[value]=!array[value]; setIsYellow(array);}} style={isYellow[value] ? style2 : style1}/></span>
</>
)
}
export default App;
This is my first time trying to code my own React app so please bear with me! I'm really struggling with the syntax and would appreciate your expertise. The code at the bottom doesn't work but I hope it helps to illustrate what I'm trying to do. Also please let me know if you believe there is a better way to structure this. Thank you in advance!
My objective is to:
Upon clicking the Submit button, the function checkPass compares the input value pass to the constant passcode.
If pass matches passcode, the state of transferReveal is updated accordingly with the value 'show' (or 'hide' if no match).
The element TransferBox is then rendered according to its class, depending on the the state of transferReveal —basically I would like the visibility of this element to toggle depending whether the user inputs the correct key.
Full code from App.js below:
import './App.css';
import React, {Component} from 'react';
class App extends Component {
constructor(props) {
super(props);
this.state = {
transferReveal: 'hide'
};
};
passcode = "24";
checkPass = () => {
this.setState((pass, passcode) => ({
pass === passcode ? {transferReveal: 'show'} : {transferReveal: 'hide'}
}));
};
render () {
return (
<div className="App">
<form>
<input
placeholder="Input private key.."
type="password"
value={pass}
/>
<input
type="submit"
value="Submit"
onClick={this.checkPass}
/>
</form>
//Check using state as className
<TransferBox className={`transferReveal`} />
</div>
);
};
}
export default App;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
I'm trying to use a vuejs custom directive called focus on a component from vuetify which is v-field-text.
directives: {
focus: {
// directive definition
inserted: function(el) {
el.focus();
}
}
}
I have a todo list, and my todos are printed with v-for, I also have an option to edit todos, whenever i click on edit button todo dispears and todo edit input apears.
I am using this focus directive to auto focusing the input.
However when i use this like this is not working:
<v-field-text v-focus></v-field-text>
But it works like this:
<input v-focus />
When i console.log the el from the directive, i see that its referring to a div element created by vuetify.
How to fix this issue?
The reason you're seeing a div when using v-focus on those elements is because they are being wrapped in a div. To get around this with third party components you don't control the code to, you may use something like the following function:
import Vue from 'vue'
Vue.directive('focus', {
inserted: function(el) {
// Recursion based function for finding an input
// nested within other elements.
let findInput = (el, max_depth = 5) => {
// We found the input, so we return it, which causes
// the entire function stack to pop
if (el.nodeName === 'INPUT') {
return el
}
// Prevent infinite recursion by providing a maximum
// depth, and returning when we've reached that depth
if (max_depth === 0) {
return null
}
// Our current element is not an input, so we need to loop
// over its children and call findInput recursively
for (let child of el.children) {
let input = findInput(child, max_depth - 1)
// We've found our input, return it to unwind the stack
// otherwise, continue through the loop
if (input) {
return input
}
}
// Fallback in case for when el has no children, or we reached the end of the loop with no input
return null
}
// Start searching for the input. We can optionally
// pass a higher max_depth. Use with caution.
let input = findInput(el, 20)
if (input) {
input.focus()
}
}
})
This is using recursion to step through each elements children, searching for an element with nodeName === 'INPUT'.
As an example, the following complex structure would be parsed and the first input found would be focused:
<div v-focus>
<div>
<div>
<div>
<div>
<div>
<div>
<div>
Hello
</div>
<p>
world
</p>
<span>!</span>
</div>
</div>
</div>
</div>
</div>
</div>
<div>
<div>
<div>
<input type="text" value="I will be focused">
</div>
</div>
</div>
</div>
Please try this solution. It's working for me:
directives: {
focus: {
// directive definition
inserted: function (el) {
let childData = el.querySelectorAll("input")[0];
childData.focus()
}
}
}
I have a custom react Input component. I want all my inputs to have a small hint beside them (an asterix) that on hover shows a hint. The problem is I cannot make the initialization of the popup point to this exact asterix so it shows the message for this particular component. Right now it just replaces the message with the message of the last mounted component.
My question is - how would I reference the exact element. Can I get the React ID from didMount? Can I point to it using render, something like $(this.render() + ' i') -> (ideal case).
import React, { Component, PropTypes } from 'react'
export default class Input extends Component {
componentDidMount() {
var html = this.props.popup;
console.log(this);
$('.inverted.asterisk.icon').popup({
html: html,
variation: 'inverted'
});
}
render() {
return (
<div className="ui icon fluid input">
<input
type={this.props.type}
value={this.props.value}
onChange={this.props.onChange}
name={this.props.name}
/>
<i className="inverted disabled asterisk link icon" />
</div>
)
}
}
Input.propTypes = {
type: PropTypes.string,
name: PropTypes.string,
popup: PropTypes.string,
value: PropTypes.node,
onChange: PropTypes.func
}
You can assign a ref attribute to any element inside your render function to get a reference to it.
Assign the ref
<i ref="icon" className="inverted disabled asterisk link icon" />
Use it in componentDidMount
componentDidMount() {
var html = this.props.popup;
console.log(this);
$(this.refs.icon).popup({
html: html,
variation: 'inverted'
});
}
jsfiddle example