У меня есть «Мой компонент React» следующим образом:
'use client';
import styles from './range-slider.module.css'
// range input = track & thumb
export default function RangeSlider({minState, maxState} :
{minState: [string, Function], maxState: [string, Function]}) {
const [min, setMin] = minState;
const [max, setMax] = maxState;
const handleMin = (e: React.ChangeEvent<HTMLInputElement>) => {
setMin(parseInt(e.target.value) < parseInt(max)? e.target.value : max);
};
const handleMax = (e: React.ChangeEvent<HTMLInputElement>) => {
setMax(parseInt(e.target.value) > parseInt(min)? e.target.value : min);
};
return (
<div>
<div className = {styles.rangeInput}>
<div className = {styles.sliderCtrl}>
<input
type = "range"
className = {`${styles.slider} ${styles.min}`}
min = "0"
max = "100"
value = {min}
onChange = {handleMin}/>
<input
type = "range"
className = {`${styles.slider} ${styles.max}`}
min = "0"
max = "100"
value = {max}
onChange = {handleMax}/>
</div>
<div className = {styles.inputCtrl}>
<div className = "min">
<input
className = "val-input"
type = "number"
min = "0"
max = "100"
value = {min}
onChange = {handleMin}/>
</div>
<div className = "max">
<input
className = "val-input"
type = "number"
min = "0"
max = "100"
value = {max}
onChange = {handleMax}/>
</div>
</div>
</div>
</div>
)
}
Однако я подозреваю, что передавать useState в качестве реквизита в RangeSlider — не лучшая практика. Мой вопрос 1: является ли это хорошей практикой и как можно это сделать.
Фактически, моя основная цель — просто получить значение RangeSlider (т. е. значение двух входных значений диапазона, вложенных в него) из его родительского компонента, я благодарен, если кто-нибудь может рассказать мне что-нибудь об этом, и это вопрос 2.
Что касается машинописного текста, не используйте Function, а вместо этого определите сигнатуру функции (value: number) => void
. Опять же, это не огромная проблема, а просто улучшение. Я подозреваю, что отрицательный голос поступил от кого-то, кто думал, что это вопрос, основанный на мнении, и он принадлежит сайту проверки кода, поскольку у вас нет конкретной проблемы, с которой вы обращаетесь за помощью.
@Адам Дженкинс Спасибо за подробный ответ и самоотверженность :). Хотя я, наконец, не принял ответ, но думаю, что почерпнул знания из вашего комментария и ответа.
используйте хук useRef для ссылки на входной элемент и получения того, что вы хотите.
import React, { useRef } from 'react';
import styles from './range-slider.module.css';
export default function RangeSlider({ minState, maxState }) {
const [min, setMin] = minState;
const [max, setMax] = maxState;
// Create refs for min and max inputs
const minInputRef = useRef(null);
const maxInputRef = useRef(null);
const handleMin = (e) => {
const value = parseInt(e.target.value);
setMin(value < parseInt(max) ? value : max);
};
const handleMax = (e) => {
const value = parseInt(e.target.value);
setMax(value > parseInt(min) ? value : min);
};
return (
<div>
<div className = {styles.rangeInput}>
<div className = {styles.sliderCtrl}>
<input
type = "range"
className = {`${styles.slider} ${styles.min}`}
min = "0"
max = "100"
value = {min}
onChange = {handleMin}
ref = {minInputRef}
/>
<input
type = "range"
className = {`${styles.slider} ${styles.max}`}
min = "0"
max = "100"
value = {max}
onChange = {handleMax}
ref = {maxInputRef}
/>
</div>
<div className = {styles.inputCtrl}>
<div className = "min">
<input
className = "val-input"
type = "number"
min = "0"
max = "100"
value = {min}
onChange = {handleMin}
ref = {minInputRef}
/>
</div>
<div className = "max">
<input
className = "val-input"
type = "number"
min = "0"
max = "100"
value = {max}
onChange = {handleMax}
ref = {maxInputRef}
/>
</div>
</div>
</div>
</div>
);
}
ОП прекрасно использует контролируемые входы. useRef необходим, когда вам нужно императивно получить значение и вы используете неконтролируемые входные данные. Это не верно.
что ты говоришь, как это неконтролируемый вход?
Смотрите мои комментарии, но я напишу это как ответ.
У вас уже есть значение в родительском компоненте.
Ваш родительский компонент (если посмотреть на API вашего RangeSlider
) должен выглядеть примерно так:
const minState = useState(0);
const maxState = useState(100);
return (
<RangerSlider minState = {minState} maxState = {maxState}/>
)
Это означает, что если вы хотите «захватить» значения в родительском компоненте, например, из-за обработчика событий, они уже есть в minState
и maxState
. Возьмите их вот так:
const minState = useState(0);
const maxState = useState(100);
const handleClick = () => {
const minValueOfRangeSlider = minState[0];
const maxValueOfRangeSlider = maxState[0];
// do something with them
}
return (
<>
<RangerSlider minState = {minState} maxState = {maxState}/>
<button onClick = {handleClick}>Submit</button>
</>
)
реквизиты, которые вы передаете в качестве значения, должны быть добавлены в родительский элемент, поэтому на самом деле теперь у вас есть нужные значения, кроме того, вы можете добавить функцию handleChangeEvent к входам onChange в качестве реквизита, например:
export default function RangeSlider ({min, max, handleChangeEvent}: {min: string, max: string, handleChangeEvent: React.ChangeEvent<HTMLInputElement>}) {
return (
<>
<input defaultValue = {min} name = "min" onChange = {handleChangeEvent}/>
<input defaultValue = {max} name = "max" onChange = {handleChangeEvent}/>
</>
)
}
и в родительском элементе вы можете справиться с этим, используя эту функцию:
export default function ParentComponent() {
const [state, setState] = useState({
min: 0,
max: 0
})
const handleChangeEvent = (e) => {
let { value, name} = e.target;
setState({...state, [name]:value})
}
return (
<>
<RangeSlider min = {state.min} max = {state.max} handleChangeEvent = {handleChangeEvent} />
</>
}
Чтобы разделить состояние между родительским компонентом и его дочерним компонентом, вы хотите, чтобы состояние «жило» в родительском компоненте, и вы хотите передать дочернему компоненту методы чтения и записи в это состояние.
Короче говоря, вы подходите к этому правильно.
Лучше всего явно передавать min
, setMin
, max
и setMax
в качестве реквизита. Это помогает сделать назначение каждого свойства более ясным. На практике, если ваш реквизит будет более плоским, это поможет вам избежать ошибок. Например, если бы вы когда-либо зависели от minState
в use
-хуке (маловероятно), вы можете обнаружить, что он обновляется чаще/реже, чем вы ожидаете, поскольку это будет зависеть от всего массива, а не только от элемента значения. .
Еще один совет: вы можете явно ввести функции set
, используя те же типы, что и React. Вы могли бы использовать React.Dispatch<React.SetStateAction<T>>
для представления функции setState
для некоторого типа T
.
Учитывая вышеизложенное, вот как я бы реорганизовал ваш код:
'use client';
import styles from './range-slider.module.css'
interface RangeSliderProps {
max: string
setMax: React.Dispatch<React.SetStateAction<T>>
min: string
setMin: React.Dispatch<React.SetStateAction<T>>
}
export default function RangeSlider({ min, setMin, max, setMax }: RangeSliderProps) {
const handleMin = (e: React.ChangeEvent<HTMLInputElement>) => {
setMin(parseInt(e.target.value) < parseInt(max) ? e.target.value : max);
};
const handleMax = (e: React.ChangeEvent<HTMLInputElement>) => {
setMax(parseInt(e.target.value) > parseInt(min) ? e.target.value : min);
};
return (
<div>
<div className = {styles.rangeInput}>
<div className = {styles.sliderCtrl}>
<input
type = "range"
className = {`${styles.slider} ${styles.min}`}
min = "0"
max = "100"
value = {min}
onChange = {handleMin}/>
<input
type = "range"
className = {`${styles.slider} ${styles.max}`}
min = "0"
max = "100"
value = {max}
onChange = {handleMax}/>
</div>
<div className = {styles.inputCtrl}>
<div className = "min">
<input
className = "val-input"
type = "number"
min = "0"
max = "100"
value = {min}
onChange = {handleMin}/>
</div>
<div className = "max">
<input
className = "val-input"
type = "number"
min = "0"
max = "100"
value = {max}
onChange = {handleMax}/>
</div>
</div>
</div>
</div>
)
}
Предпосылка того, что вы делаете, абсолютно хороша. Синтаксис, возможно, немного неуклюж. Я предпочитаю передавать конкретные имена свойств, а не просто передавать состояние напрямую (даже если результат будет идентичным). Рекомендуется передавать 4 явных имени реквизита — minValue, maxValue, onChangeMin и onChangeMax. Возможно, вы могли бы переместить логику этого компонента обработки изменений значений в родительский компонент. Однако при этом вы не допустили никаких вопиющих ошибок.