React Typescript - Math.random 即使设置了最小值和最大值也会给出错误的结果
React Typescript - Math.random gives wrong result even tho min value and max value is set
TSX 主文件
const [minNumber, setMinNumber] = useLocalStorage('minRange', 1);
const [maxNumber, setMaxNumber] = useLocalStorage('maxRange', 10);
...
const getRandomNumber = () => {
//Return a random number between minNumber and maxNumber:
console.log("min: " + minNumber + " max: " + maxNumber)
let randomNr = Math.floor(Math.random() * (maxNumber - minNumber + 1) + minNumber);
setRandomNumber(randomNr);
}
本地存储挂钩:
import { useState } from 'react';
export function useLocalStorage(key, defaultValue) {
const getInitialValue = () => localStorage.getItem(key) ?? defaultValue;
const [value, setValue] = useState(getInitialValue);
const setAndStoreValue = (newValue) => {
if (newValue !== '') {
setValue(newValue);
localStorage.setItem(key, newValue)
}
}
return [value, setAndStoreValue];
};
演示问题。我创建了一个 codesandbox link:
https://codesandbox.io/s/hdwqy?file=/src/App.tsx
如何重现问题:
- 默认情况下,最小值为 1,最大值为 10
- 将最小值设置为 8,将最大值设置为 9(单击 codesandbox 中的“更改最小值和最大值”按钮)
- 运行 getRandomNumber() 函数。
Outputs: 8 or 9
- 有效! (点击codesandbox中的“New random Number”按钮)
- 刷新页面,你会得到本地存储的数据 min = 8, max = 9
- 运行 getRandomNumber() 函数。
outputs 0 or 1
- 输出错误! (点击codesandbox中的“New random Number”按钮)
- 问题是因为最小值和最大值之间的差值为 2,所以它给出 0 和 1。如果最小值为 8,最大值为 10,那么它将输出 0、1 或 2。
我似乎无法弄清楚为什么会这样,因为如果我再次设置最小值和最大值并重新运行 随机函数,它就可以正常工作。我还想提一下,在这两种情况下,console.log 输出 min: 8 max: 9
,因此看起来本地存储正在正确加载值。很奇怪。
问题可以简化为:
const minNumber = '8';
const maxNumber = '9';
const getRandomNumber = () => {
console.log("min: " + minNumber + " max: " + maxNumber);
console.log('part', maxNumber - minNumber + 1);
const result = Math.random() * 2 + '8';
console.log(result);
};
getRandomNumber();
本地存储值总是字符串。这个:
let randomNr = Math.floor(
Math.random() * (maxNumber - minNumber + 1) + minNumber
);
变成,8和9:
let randomNr = Math.floor(
Math.random() * 2 + '8'
);
变成类似 0.234458
或 1.63138
的东西 - +
将尾随 8 连接到 decimal 部分,然后是 Math.floor
剃掉它,所以你仍然剩下一个 0 或 1 的数字。
首先将存储值转换为数字,也许通过 useLocalStorage
:
的映射器函数
export function useLocalStorage(key, defaultValue, mapper = str => str) {
const getInitialValue = () => localStorage.getItem(key) !== null
? mapper(localStorage.getItem(key))
: defaultValue;
const [value, setValue] = useState(getInitialValue);
const setAndStoreValue = (newValue) => {
if (newValue !== '') {
setValue(newValue);
localStorage.setItem(key, newValue)
}
}
return [value, setAndStoreValue];
};
const [minNumber, setMinNumber] = useLocalStorage('minRange', 1, Number);
const [maxNumber, setMaxNumber] = useLocalStorage('maxRange', 10, Number);
这个问题可以通过更多地使用 TypeScript 来避免,并正确地输入你的变量(避免 any
- 这违背了 TypeScript 的要点) - minNumber
和 [=23= 的类型] 是一个 字符串或数字 而不仅仅是一个数字会让你失望。
也使用泛型:
function useLocalStorage<T>(
key: string,
defaultValue: T,
mapperToValue: (lsValue: string) => T,
mapperToLS: (value: T) => string
) {
const getInitialValue = () => {
const lsValue = localStorage.getItem(key);
return lsValue !== null ? mapperToValue(lsValue) : defaultValue;
};
const [value, setValue] = useState(getInitialValue);
const setAndStoreValue = (newValue: T) => {
setValue(newValue);
localStorage.setItem(key, mapperToLS(newValue));
};
return [value, setAndStoreValue] as const;
}
export default function App() {
const [minNumber, setMinNumber] = useLocalStorage("minRange", 1, Number, String);
const [maxNumber, setMaxNumber] = useLocalStorage("maxRange", 10, Number, String);
const [rndNumber, setRndNumber] = useState(0);
// ...
TSX 主文件
const [minNumber, setMinNumber] = useLocalStorage('minRange', 1);
const [maxNumber, setMaxNumber] = useLocalStorage('maxRange', 10);
...
const getRandomNumber = () => {
//Return a random number between minNumber and maxNumber:
console.log("min: " + minNumber + " max: " + maxNumber)
let randomNr = Math.floor(Math.random() * (maxNumber - minNumber + 1) + minNumber);
setRandomNumber(randomNr);
}
本地存储挂钩:
import { useState } from 'react';
export function useLocalStorage(key, defaultValue) {
const getInitialValue = () => localStorage.getItem(key) ?? defaultValue;
const [value, setValue] = useState(getInitialValue);
const setAndStoreValue = (newValue) => {
if (newValue !== '') {
setValue(newValue);
localStorage.setItem(key, newValue)
}
}
return [value, setAndStoreValue];
};
演示问题。我创建了一个 codesandbox link: https://codesandbox.io/s/hdwqy?file=/src/App.tsx
如何重现问题:
- 默认情况下,最小值为 1,最大值为 10
- 将最小值设置为 8,将最大值设置为 9(单击 codesandbox 中的“更改最小值和最大值”按钮)
- 运行 getRandomNumber() 函数。
Outputs: 8 or 9
- 有效! (点击codesandbox中的“New random Number”按钮) - 刷新页面,你会得到本地存储的数据 min = 8, max = 9
- 运行 getRandomNumber() 函数。
outputs 0 or 1
- 输出错误! (点击codesandbox中的“New random Number”按钮) - 问题是因为最小值和最大值之间的差值为 2,所以它给出 0 和 1。如果最小值为 8,最大值为 10,那么它将输出 0、1 或 2。
我似乎无法弄清楚为什么会这样,因为如果我再次设置最小值和最大值并重新运行 随机函数,它就可以正常工作。我还想提一下,在这两种情况下,console.log 输出 min: 8 max: 9
,因此看起来本地存储正在正确加载值。很奇怪。
问题可以简化为:
const minNumber = '8';
const maxNumber = '9';
const getRandomNumber = () => {
console.log("min: " + minNumber + " max: " + maxNumber);
console.log('part', maxNumber - minNumber + 1);
const result = Math.random() * 2 + '8';
console.log(result);
};
getRandomNumber();
本地存储值总是字符串。这个:
let randomNr = Math.floor(
Math.random() * (maxNumber - minNumber + 1) + minNumber
);
变成,8和9:
let randomNr = Math.floor(
Math.random() * 2 + '8'
);
变成类似 0.234458
或 1.63138
的东西 - +
将尾随 8 连接到 decimal 部分,然后是 Math.floor
剃掉它,所以你仍然剩下一个 0 或 1 的数字。
首先将存储值转换为数字,也许通过 useLocalStorage
:
export function useLocalStorage(key, defaultValue, mapper = str => str) {
const getInitialValue = () => localStorage.getItem(key) !== null
? mapper(localStorage.getItem(key))
: defaultValue;
const [value, setValue] = useState(getInitialValue);
const setAndStoreValue = (newValue) => {
if (newValue !== '') {
setValue(newValue);
localStorage.setItem(key, newValue)
}
}
return [value, setAndStoreValue];
};
const [minNumber, setMinNumber] = useLocalStorage('minRange', 1, Number);
const [maxNumber, setMaxNumber] = useLocalStorage('maxRange', 10, Number);
这个问题可以通过更多地使用 TypeScript 来避免,并正确地输入你的变量(避免 any
- 这违背了 TypeScript 的要点) - minNumber
和 [=23= 的类型] 是一个 字符串或数字 而不仅仅是一个数字会让你失望。
也使用泛型:
function useLocalStorage<T>(
key: string,
defaultValue: T,
mapperToValue: (lsValue: string) => T,
mapperToLS: (value: T) => string
) {
const getInitialValue = () => {
const lsValue = localStorage.getItem(key);
return lsValue !== null ? mapperToValue(lsValue) : defaultValue;
};
const [value, setValue] = useState(getInitialValue);
const setAndStoreValue = (newValue: T) => {
setValue(newValue);
localStorage.setItem(key, mapperToLS(newValue));
};
return [value, setAndStoreValue] as const;
}
export default function App() {
const [minNumber, setMinNumber] = useLocalStorage("minRange", 1, Number, String);
const [maxNumber, setMaxNumber] = useLocalStorage("maxRange", 10, Number, String);
const [rndNumber, setRndNumber] = useState(0);
// ...