从useState钩取值

huangapple go评论74阅读模式
英文:

Retrieving value from useState hook

问题

以下是您要翻译的内容:

I have a problem regarding my current function `checkResultOfCards`. This function is unable to retrieve an updated value (`currentAnswer`) from a `useState`-Hook which I update through a TextInput. My code sample may be long but consider that half of it are `useEffect`-hooks which render only once under a special condition. That said, here is my code so far:

const PracticeScreen = props => {
  const dispatch = useDispatch();

  const myLibrary = useSelector(state => state.myLibrary.myLibrary);
  const myBottomTab = useSelector(state => state.myOtherReducers.showBottomTab);

  const backActionRef = useRef(null);
  const swiperRef = useRef(null);

  const [loadedCardStack, setLoadedCardStack] = useState([]);
  const [modalVisible, setModalVisible] = useState(false);
  const [currentAnswer, setCurrentAnswer] = useState("");

  const [wrongCards, setWrongCards] = useState([]);
  const [goodCards, setGoodCards] = useState([]);
  const [isAnswerRight, setIsAnswerRight] = useState(null);

  useEffect(() => {
    /**listener sub and unsub when changing screen
     * dispatching of bottom tab nav happening here*/
    const backAction = () => {
      Alert.alert("Hold on!", "Are you sure you want to exit the course?", [
        {
          text: "Cancel",
          onPress: () => null,
          style: "cancel"
        },
        { text: "YES", onPress: () => {
          dispatch(SpecialActions.setBottomTabVisibility("flex"));
          props.navigation.goBack()
        }}
      ],{cancelable: false});
      return true;
    };

    backActionRef.current = backAction;

    const backHandler = () => {
      backActionRef.current();
      return true;
    };

    // event listener when the screen comes into focus
    const focusListener = props.navigation.addListener('focus', () => {
      BackHandler.addEventListener("hardwareBackPress", backHandler);
    });

    // removing of the listener when the screen goes out of focus
    const blurListener = props.navigation.addListener('blur', () => {
      dispatch(SpecialActions.setBottomTabVisibility("flex"));
      BackHandler.removeEventListener("hardwareBackPress", backHandler);
    });

    return () => {
      focusListener();
      blurListener();
      BackHandler.removeEventListener("hardwareBackPress", backHandler);
    }
  }, [props.navigation]);

  useLayoutEffect(() => {
    /*Layout styling for the header must be here, not in Navigator.js*/
    props.navigation.setOptions({
      headerTitle: 'Practice Screen',
      headerStyle: {
        backgroundColor: '#7dbae5',
        borderBottomWidth: 0.5,
        borderBottomColor: 'black',
      },
      headerTitleStyle: {
        color: 'black',
      },
      headerLeft: () => (
        <TouchableOpacity onPress={() => {
          Alert.alert("Hold on!", "Are you sure you want to exit the course?", [
            {
              text: "Cancel",
              onPress: () => null,
              style: "cancel"
            },
            { text: "YES", onPress: () => {
              dispatch(SpecialActions.setBottomTabVisibility("flex"));
              props.navigation.goBack()}}
          ],{cancelable: false});
        }}>
          <AntDesign name="caretleft" size={24} color="black" />
        </TouchableOpacity>
      ),
      headerRight: () => (
        <TouchableOpacity onPress={() => {
          setModalVisible(!modalVisible)
        }}>
          <AntDesign name="infocirlce" size={24} color="black" />
        </TouchableOpacity>
      )
    });
  }, [props.navigation]);

  useEffect(() => {
    /*loading up the Cards */ 
    loadCardStack();
  }, []);


  const loadCardStack = () => {
    setLoadedCardStack(props.route.params.courseData.cards);
    console.log("here is your stack :",loadedCardStack)
  };
  console.log("RERENDER") // checking if after each typing into TextInput this log triggers, and yes it does and so must the useState be updated
  const checkResultOfCard = (rightAnswer) => {
    // if wrong question, mark card, else add to positive stack
    if(currentAnswer !== rightAnswer){
      setIsAnswerRight(false);
      console.log("wrong answer: ", currentAnswer, "also the type: ", typeof(currentAnswer))
    }else{
      setIsAnswerRight(true);
      console.log("good answer")
    }
  };

  return (
    <View style={{flex:1}}>
      <KeyboardAvoidingView 
        style={styles.container} 
        behavior='height' 
        keyboardVerticalOffset={-(Dimensions.get('window').height * 0.18)} 
        enabled
      >
        {loadedCardStack.length > 0 && (
        <CardStack 
          style={styles.CardStackStyle} 
          ref={swiperRef}
          loop={true}
          verticalSwipe={false}
        >
          {loadedCardStack.map((card, index) => (
            <Card key={card.q1} style={styles.cardStyle1}>
              <View style={styles.splitCard}>
                <Text style={styles.question1}>{card.q1}</Text>
                <TextInput 
                  style={styles.textEdit1}
                  placeholder=" answer"
                  onChangeText={text => {
                    setCurrentAnswer(text)//THIS IS WHERE I CHANGE THE STATE
                  }}
                />
                <TouchableOpacity
                  style={styles.buttonStyle}
                  onPress={() => {
                    checkResultOfCard(card.answer); //HERE I CALL THE FUNCION WHICH FAILS ITS TASK
                  }}
                >
                <Text>Check Result</Text>
                </TouchableOpacity>
              </View>
              <View style={styles.splitCard}>
               
              </View>
            </Card>
          ))}
        </CardStack>
        )}
        <StatusBar style="auto" />
      </KeyboardAvoidingView>
    </View>
  );
}

It probably has something to do with the State being updated but the function not recognizing the new value of the Hook. I tried to write the code from scratch and try out a demo on my own, here is a short example:

const MyComponent = () => {
  const [text, setText] = useState('');
  const myfunction = () =>{
    alert(text)
  };

  return (
    <View>
      <TextInput
        onChangeText={text => setText(text)//HERE I CHANGE THE STATE}
      />
      <Button
        title="Retrieve Text"
        onPress={() => {
          myfunction();//HERE I CALL THE FUNCTION, IT WORKS
        }}
      />
    </View>
  );
};

It is the same logic with also a pressable object firing a function but in this case everything works perfectly! That's why I am confused and reaching out for your help and some tips on how I can fix my code to make it work like in the demo, the second example.

EDIT: [This is the Card-Package][1] I am using for the `<CardStack>` and `<Card>` elements. Also, it is **mandatory** that I somehow make the `useState`-hooks working. I plan to add features like changing the color of the `<Card>` and this won't be possible without rerendering, which should be triggered by a change in a State.

[1]: https://www.n

<details>
<summary>英文:</summary>

I have a problem regarding my current function `checkResultOfCards`. This function is unable to retrieve a updated value (`currentAnswer`) from a `useState`-Hook which I update through a TextInput. My codesample may be long but consider that half of it are `useEffect`-hooks which render only once under a special condition. That said, here is my code so far: 

    const PracticeScreen = props =&gt; {
      const dispatch = useDispatch();
    
      const myLibrary = useSelector(state =&gt; state.myLibrary.myLibrary);
      const myBottomTab = useSelector(state =&gt; state.myOtherReducers.showBottomTab);
    
      const backActionRef = useRef(null);
      const swiperRef = useRef(null);
    
      const [loadedCardStack, setLoadedCardStack] = useState([]);
      const [modalVisible, setModalVisible] = useState(false);
      const [currentAnswer, setCurrentAnswer] = useState(&quot;&quot;);
    
      const [wrongCards, setWrongCards] = useState([]);
      const [goodCards, setGoodCards] = useState([]);
      const [isAnswerRight, setIsAnswerRight] = useState(null);
    
      useEffect(() =&gt; {
        /**listener sub and unsub when changing screen
         * dispatching of bottom tab nav happening here*/
        const backAction = () =&gt; {
          Alert.alert(&quot;Hold on!&quot;, &quot;Are you sure you want to exit the course?&quot;, [
            {
              text: &quot;Cancel&quot;,
              onPress: () =&gt; null,
              style: &quot;cancel&quot;
            },
            { text: &quot;YES&quot;, onPress: () =&gt; {
              dispatch(SpecialActions.setBottomTabVisibility(&quot;flex&quot;));
              props.navigation.goBack()
            }}
          ],{cancelable: false});
          return true;
        };
    
        backActionRef.current = backAction;
    
        const backHandler = () =&gt; {
          backActionRef.current();
          return true;
        };
    
        // event listener when the screen comes into focus
        const focusListener = props.navigation.addListener(&#39;focus&#39;, () =&gt; {
          BackHandler.addEventListener(&quot;hardwareBackPress&quot;, backHandler);
        });
    
        // removing of the listener when the screen goes out of focus
        const blurListener = props.navigation.addListener(&#39;blur&#39;, () =&gt; {
          dispatch(SpecialActions.setBottomTabVisibility(&quot;flex&quot;));
          BackHandler.removeEventListener(&quot;hardwareBackPress&quot;, backHandler);
        });
    
        return () =&gt; {
          focusListener();
          blurListener();
          BackHandler.removeEventListener(&quot;hardwareBackPress&quot;, backHandler);
        }
      }, [props.navigation]);
    
      useLayoutEffect(() =&gt; {
        /*Layoutstyling for header must be here, not in Navigator.js*/
        props.navigation.setOptions({
          headerTitle: &#39;Practice Screen&#39;,
          headerStyle: {
            backgroundColor: &#39;#7dbae5&#39;,
            borderBottomWidth: 0.5,
            borderBottomColor: &#39;black&#39;,
          },
          headerTitleStyle: {
            color: &#39;black&#39;,
          },
          headerLeft: () =&gt; (
            &lt;TouchableOpacity onPress={() =&gt; {
              Alert.alert(&quot;Hold on!&quot;, &quot;Are you sure you want to exit the course?&quot;, [
                {
                  text: &quot;Cancel&quot;,
                  onPress: () =&gt; null,
                  style: &quot;cancel&quot;
                },
                { text: &quot;YES&quot;, onPress: () =&gt; {
                  dispatch(SpecialActions.setBottomTabVisibility(&quot;flex&quot;));
                  props.navigation.goBack()}}
              ],{cancelable: false});
            }}&gt;
              &lt;AntDesign name=&quot;caretleft&quot; size={24} color=&quot;black&quot; /&gt;
            &lt;/TouchableOpacity&gt;
          ),
          headerRight: () =&gt; (
            &lt;TouchableOpacity onPress={() =&gt; {
              setModalVisible(!modalVisible)
            }}&gt;
              &lt;AntDesign name=&quot;infocirlce&quot; size={24} color=&quot;black&quot; /&gt;
            &lt;/TouchableOpacity&gt;
          )
        });
      }, [props.navigation]);
    
      useEffect(() =&gt; {
        /*loading up the Cards */ 
        loadCardStack();
      }, []);
    
    
      const loadCardStack = () =&gt; {
        setLoadedCardStack(props.route.params.courseData.cards);
        console.log(&quot;here is your stack :&quot;,loadedCardStack)
      };
      console.log(&quot;RERENDER&quot;)//checking if after each typing into TextInput this log triggers, and yes it does and so must the useState be updated
      const checkResultOfCard = (rightAnswer) =&gt; {
        //if wrong question, mark card, else add to positive stack
        if(currentAnswer !== rightAnswer){
          setIsAnswerRight(false);
          console.log(&quot;wrong answer: &quot;, currentAnswer, &quot;also the type: &quot;, typeof(currentAnswer))
        }else{
          setIsAnswerRight(true);
          console.log(&quot;good answer&quot;)
        }
      };
    
      return (
        &lt;View style={{flex:1}}&gt;
          &lt;KeyboardAvoidingView 
            style={styles.container} 
            behavior=&#39;height&#39; 
            keyboardVerticalOffset={-(Dimensions.get(&#39;window&#39;).height * 0.18)} 
            enabled
          &gt;
            {loadedCardStack.length &gt; 0 &amp;&amp; (
            &lt;CardStack 
              style={styles.CardStackStyle} 
              ref={swiperRef}
              loop={true}
              verticalSwipe={false}
            &gt;
              {loadedCardStack.map((card, index) =&gt; (
                &lt;Card key={card.q1} style={styles.cardStyle1}&gt;
                  &lt;View style={styles.splitCard}&gt;
                    &lt;Text style={styles.question1}&gt;{card.q1}&lt;/Text&gt;
                    &lt;TextInput 
                      style={styles.textEdit1}
                      placeholder=&quot; answer&quot;
                      onChangeText={text =&gt; {
                        setCurrentAnswer(text)//THIS IS WHERE I CHANGE THE STATE
                      }}
                    /&gt;
                    &lt;TouchableOpacity
                      style={styles.buttonStyle}
                      onPress={() =&gt; {
                        checkResultOfCard(card.answer); //HERE I CALL THE FUNCION WHICH FAILS ITS TASK
                      }}
                    &gt;
                    &lt;Text&gt;Check Result&lt;/Text&gt;
                    &lt;/TouchableOpacity&gt;
                  &lt;/View&gt;
                  &lt;View style={styles.splitCard}&gt;
                   
                  &lt;/View&gt;
                &lt;/Card&gt;
              ))}
            &lt;/CardStack&gt;
            )}
            &lt;StatusBar style=&quot;auto&quot; /&gt;
          &lt;/KeyboardAvoidingView&gt;
        &lt;/View&gt;
      );
    }

It probably has something to do with the State beeing updated but the function not recognizing the new value of the Hook. I tried to write the code from scratch and try out a demo on my own, here is a short example:

    const MyComponent = () =&gt; {
      const [text, setText] = useState(&#39;&#39;);
      const myfunction = () =&gt;{
        alert(text)
      };
    
      return (
        &lt;View&gt;
          &lt;TextInput
            onChangeText={text =&gt; setText(text)//HERE I CHANGE THE STATE}
          /&gt;
          &lt;Button
            title=&quot;Retrieve Text&quot;
            onPress={() =&gt; {
              myfunction();//HERE I CALL THE FUNCTION, IT WORKS
            }}
          /&gt;
        &lt;/View&gt;
      );
    };

It is the same logic with also a pressable object firing a function but in this case everything works perfectly! Thats why I am confuced and reaching out for your help and some tips how I can fix my code to make it work like in the demo, the second example. 

EDIT: [This is the Card-Package][1] I am using for the `&lt;CardStack&gt;` and `&lt;Card&gt;` elements. Also it is **mandatory** that I somehow make the `useState`-hooks working. I plan to add features like changing color of the `&lt;Card&gt;` and this wont be possible without rerendering, which should be triggered by an change in a State. 


  [1]: https://www.npmjs.com/package/react-native-card-stack-swiper

</details>


# 答案1
**得分**: 1

我复制了您的代码以重新创建错误似乎可以正常工作请查看下面的代码示例

```jsx
function App() {
  const loadedCardStack = [
    {
      q1: "What is your name?",
      answer: "john"
    }
  ];

  const [currentAnswer, setCurrentAnswer] = useState("");

  const [isAnswerRight, setIsAnswerRight] = useState(null);

  const checkResultOfCard = (rightAnswer) => {
    //if wrong question, mark card, else add to positive stack
    setIsAnswerRight(
      rightAnswer && currentAnswer.trim().toLowerCase() === rightAnswer.trim().toLowerCase()
    );
  };

  return (
    <View style={styles.app}>
      {loadedCardStack.map((card, index) => (
        <View key={index}>
          <Text>{card.q1}</Text>
          <TextInput
            style={{ borderColor: "#ddd", borderWidth: 1, margin: 10 }}
            placeholder="answer"
            onChangeText={(text) => {
              setCurrentAnswer(text); //这是我更改状态的地方
            }}
          />
          <TouchableOpacity
            onPress={() => {
              checkResultOfCard(card.answer); //这里我调用了可能失败的函数
            }}
            style={{ marginTop: 7, backgroundColor: "orange" }}
          >
            <Text>检查结果</Text>
          </TouchableOpacity>
        </View>
      ))}
      <Text>答案: {isAnswerRight ? "正确" : "不正确"}</Text>
    </View>
  );
}

您可以在此链接中查看实际效果:https://codesandbox.io/s/distracted-sea-xn5ikq?file=/src/App.js:146-1373

我不确定为什么对您不起作用。除了 checkResultOfCard 函数之外,我几乎没有做任何更改。

请注意,对于您的代码,您必须以正确的大小写输入答案。例如:如果答案是 John,而您输入 john,它将被标记为不正确。因此,我更改了函数以在比较之前将字符串转换为小写。另外,我删除了两端的空格。如果有帮助,请告诉我。

英文:

I copied your code to recreate the error and it seems to work. See the code sample below:

function App() {
const loadedCardStack = [
{
q1: &quot;What is your name?&quot;,
answer: &quot;john&quot;
}
];
const [currentAnswer, setCurrentAnswer] = useState(&quot;&quot;);
const [isAnswerRight, setIsAnswerRight] = useState(null);
const checkResultOfCard = (rightAnswer) =&gt; {
//if wrong question, mark card, else add to positive stack
setIsAnswerRight(
rightAnswer &amp;&amp;
currentAnswer.trim().toLowerCase() === rightAnswer.trim().toLowerCase()
);
};
return (
&lt;View style={styles.app}&gt;
{loadedCardStack.map((card, index) =&gt; (
&lt;View&gt;
&lt;Text&gt;{card.q1}&lt;/Text&gt;
&lt;TextInput
style={{ borderColor: &quot;#ddd&quot;, borderWidth: 1, margin: 10 }}
placeholder=&quot;answer&quot;
onChangeText={(text) =&gt; {
setCurrentAnswer(text); //THIS IS WHERE I CHANGE THE STATE
}}
/&gt;
&lt;TouchableOpacity
onPress={() =&gt; {
checkResultOfCard(card.answer); //HERE I CALL THE FUNCION WHICH FAILS ITS TASK
}}
style={{ marginTop: 7, backgroundColor: &quot;orange&quot; }}
&gt;
&lt;Text&gt;Check Result&lt;/Text&gt;
&lt;/TouchableOpacity&gt;
&lt;/View&gt;
))}
&lt;Text&gt;Answer: {isAnswerRight ? &quot;Correct&quot; : &quot;Incorrect&quot;}&lt;/Text&gt;
&lt;/View&gt;
);
}

You can see it live here: https://codesandbox.io/s/distracted-sea-xn5ikq?file=/src/App.js:146-1373

I'm not sure why it doesn't work for you. I barely made any changes except for the checkResultOfCard function.

Note that for your code, you have to type the answer in the correct case. i.e.: If the answer is John and you type john it will be marked as incorrect. So I changed the function to convert the string to lowercase before comparing. Also, I trimmed the white spaces from both ends.

Let me know if this helps.

答案2

得分: 0

你需要在这种情况下使用数组。

// 放在全局
let arrayAnswer = Array();

// 更改checkResultOfCard函数
const checkResultOfCard = (rightAnswer, index) => {
// 如果答案错误,标记卡片,否则添加到正面堆栈
if (arrayAnswer[index] !== rightAnswer) {
setIsAnswerRight(false);

    console.log('array : ', arrayAnswer[index]);
} else {
setIsAnswerRight(true);
console.log('好答案');
}

};

// 最后在TextInput上设置数组
<TextInput
style={styles.textEdit1}
placeholder="答案"
onChangeText={(text) => {
let data;
arrayAnswer[index] = text;
console.log('arrayAnswer', arrayAnswer, index)

    setCurrentAnswer(text); //这是我更改状态的地方
}}

/>

英文:

you need to use array for this case.

// put this on global
let arrayAnswer = Array();
// change checkResultOfCard  function
const checkResultOfCard = (rightAnswer, index) =&gt; {
//if wrong question, mark card, else add to positive stack
if (arrayAnswer[index] !== rightAnswer) {
setIsAnswerRight(false);
console.log(&#39;array : &#39;, arrayAnswer[index]);
} else {
setIsAnswerRight(true);
console.log(&#39;good answer&#39;);
}
};
//and lastly put the set on array on textinput
&lt;TextInput
style={styles.textEdit1}
placeholder=&quot; answer&quot;
onChangeText={(text) =&gt; {
let data;
arrayAnswer[index] = text;
console.log(&#39;arrayAnswer&#39;,arrayAnswer, index)
setCurrentAnswer(text); //THIS IS WHERE I CHANGE THE STATE
}}
/&gt;

答案3

得分: 0

我已附上我的代码在 Snack 上。
https://snack.expo.dev/@jonasbeck.dev/retrieving-value-from-usestate-hook
我猜问题的原因可能是 Card 组件将状态值作为常数与初始值一起引用。
让我尝试用一个类似的案例来解释。

在下面的示例代码中,'t' 的值始终为1,而不是每秒递增1,因为 't' 在 'setInterval' 的参数函数中被引用为常数0。

const [t, setT] = useState(0)
setInterval(() => {
setT(t+1)
}, 1000)

解决方案是使用 setState 回调,像这样:

const [t, setT] = useState(0)
setInterval(() => {
setT(prevState => prevState+1)
}, 1000)

就像这样,我可以修复您的代码。

const checkResultOfCard = (rightAnswer) => {
// 如果问题错误,标记卡片,否则添加到正面堆栈
setRightAnswer(prevState => {
setIsAnswerRight(
rightAnswer &&
currentAnswer.trim().toLowerCase() === rightAnswer.trim().toLowerCase()
);
return prevState;
});
};

英文:

Attached my code on snack.
https://snack.expo.dev/@jonasbeck.dev/retrieving-value-from-usestate-hook
I guess the reason of issue would be Card component refer state value as constant with initial value.
Let me try to explain with a similar case.

With the example code below, the value of 't' is always 1 not increazing value per 1 sec because 't' is referred to constant 0 inside the parameter function of 'setInterval'.

> const [t, setT] = useState(0)
> setInterval(()=>{
> setT(t+1)
> }, 1000)

The solution is to use setState callback like this:

> const [t, setT] = useState(0)
> setInterval(()=>{
> setT(prevState=>prevState+1)
> }, 1000)

like that I can fix your code.

> const checkResultOfCard = (rightAnswer) => {
> //if wrong question, mark card, else add to positive stack
> setRightAnswer(prevState=>{
> setIsAnswerRight(
> rightAnswer &&
> currentAnswer.trim().toLowerCase() === rightAnswer.trim().toLowerCase()
> );
> return prevState;
> });
> };

答案4

得分: 0

一般来说,直接从输入中更新状态是一种不好的做法。创建一个具有文本输入状态更新的去抖处理函数。

英文:

In general it's a bad practise to directly update state from an input. Create a handle function with a debounce for the text input state update.

答案5

得分: 0

问题出在这里:https://github.com/lhandel/react-native-card-stack-swiper/blob/master/CardStack.js#L154

当顶部组件重新渲染(由于setCurrentAnswer的调用),CardStack也重新渲染,并调用其componentDidUpdate(因为其children属性实际上发生了变化)。

但然后它确定子组件是相同的(因为数组长度相同,所有子组件的key保持相同并且顺序相同)。因此,它跳过更新其状态(即cardAcardB属性)。

而这些属性是在render方法中使用的,而不是直接使用children属性(https://github.com/lhandel/react-native-card-stack-swiper/blob/master/CardStack.js#L404)。

它将呈现旧的子组件集,而不是新的子组件集,因此TouchableOpacity使用的是旧版本的checkResultOfCard引用。请注意,对于输入框来说这并不是问题(输入的文本不会被替换),因为它是一个本机HTML元素,根据设计是不受控制的。

这基本上是react-native-card-stack-swiper的一个不良实现。您应该打开一个错误报告或提交一个修复。与此同时,作为一种解决方法,您可以尝试通过更改卡片的键(key={card.q1 + A_COUNTER_OR_SOMETHING_ELSE})来强制执行完整的重新渲染。或者更改checkResultOfCard的实现,直接从输入框中获取答案值(使用引用)。

英文:

The problem is here: https://github.com/lhandel/react-native-card-stack-swiper/blob/master/CardStack.js#L154

When the top component re-renders (due to the setCurrentAnswer call), the CardStack re-renders and its componentDidUpdate is called (because its children prop effectively changed).

But it then determines that the children are the same (because the array length is the same and all the childs' key remain the same and in the same order). So it skips updating its state (namely the cardA and cardB properties).

And those are the properties used at the render method, instead of directly the children property (https://github.com/lhandel/react-native-card-stack-swiper/blob/master/CardStack.js#L404).

It will render the old set of childs instead of the new one, so the TouchableOpacity is one with an old reference of checkResultOfCard in its closure. Notice it is not a problem for the input (the inputted text doesn't get replaced) because it is a native html element, uncontrolled by design.

This basically is a poor implementation by react-native-card-stack-swiper. You should open a bug or submit a fix. Meanwhile, as a workaround, you may try to force the full re-render by changing the card key (key={card.q1 + A_COUNTER_OR_SOMETHING_ELSE}). Or change the implementation of checkResultOfCard for fetching the answer value directly from the input (using a ref).

huangapple
  • 本文由 发表于 2023年2月27日 09:53:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/75576189.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定