Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/reactjs/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 如果连续单击两个相同的名称,如何使它们保持红色?_Javascript_Reactjs - Fatal编程技术网

Javascript 如果连续单击两个相同的名称,如何使它们保持红色?

Javascript 如果连续单击两个相同的名称,如何使它们保持红色?,javascript,reactjs,Javascript,Reactjs,我用列表项表示名称,当单击任何名称时,它会变为红色,然后花一秒钟时间再次返回黑色,但连续单击两个相同的名称会使它们保持红色,而不会再次变为黑色 你可以把它想象成一个记忆游戏,但我试着在这里举一个简单的例子,说明我在最初的项目中试图实现的目标 这是我的代码和错误的尝试: const App = () => { const { useState } = React; const items = [ { name: 'm

我用列表项表示名称,当单击任何名称时,它会变为红色,然后花一秒钟时间再次返回黑色,但连续单击两个相同的名称会使它们保持红色,而不会再次变为黑色

你可以把它想象成一个记忆游戏,但我试着在这里举一个简单的例子,说明我在最初的项目中试图实现的目标

这是我的代码和错误的尝试:

const App = () => {
  const { useState } = React;
        const items = [
            {
                name: 'mark',
                id: 1,
                red: false
            },
            {
                name: 'peter',
                id: 2,
                red: false
            },
            {
                name: 'john',
                id: 3,
                red: false
            },

            {
                name: 'mark',
                id: 4,
                red: false,
            },
            {
                name: 'peter',
                id: 5,
                red: false
            },
            {
                name: 'john',
                id: 6,
                red: false
            }
        ];

        const [names, setNames] = useState(items);
        const [firstName, setFirstName] = useState(null);
        const [secondName, setSecondName] = useState(null)

        const handleItemClick = (item) => {
            setNames(prev => prev.map(i => i.id === item.id ? { ...i, red: true } : i));
            //the problem is here
            setTimeout(() => {
                setNames(prev => prev.map(n => {
                    if (secondName && (secondName.name === firstName.name) && n.name === firstName.name) {
                        return { ...n,red: true }
                    }

                    return { ...n, red: false };
                }))
            }, 1000)

            if (!firstName) setFirstName(item);
            else if (firstName && !secondName) setSecondName(item)
            else if (firstName && secondName) {
                setFirstName(item);
                setSecondName(null)
            }
        }


        return (
            <div class="app">
                <ul class="items">
                    {
                        names.map(i => {
                            return (
                                <Item
                                    item={i}
                                    handleItemClick={handleItemClick}
                                />
                            )
                        })
                    }
                </ul>
            </div>
        )

    }


    const Item = ({ item, ...props }) => {
        const { id, name, red } = item;
        const { handleItemClick } = props;

        return (
            <li
                className={`${red ? 'red' : ''}`}
                onClick={() => handleItemClick(item)}
            >
                {name}
            </li>
        )
    }

    ReactDOM.render(<App />, document.getElementById('root'))

const-App=()=>{
const{useState}=React;
常数项=[
{
姓名:'马克',
id:1,
红色:错
},
{
姓名:'彼得',
id:2,
红色:错
},
{
姓名:'约翰',
id:3,
红色:错
},
{
姓名:'马克',
id:4,
瑞德:错,
},
{
姓名:'彼得',
id:5,
红色:错
},
{
姓名:'约翰',
id:6,
红色:错
}
];
const[names,setNames]=useState(项);
const[firstName,setFirstName]=useState(null);
常量[secondName,setSecondName]=useState(null)
常量handleItemClick=(项目)=>{
集合名(prev=>prev.map(i=>i.id==item.id?{…i,红色:true}:i));
//问题就在这里
设置超时(()=>{
集合名(prev=>prev.map(n=>{
if(secondName&(secondName.name==firstName.name)&&n.name==firstName.name){
返回{…n,红色:true}
}
返回{…n,红色:false};
}))
}, 1000)
如果(!firstName)setFirstName(项);
else if(firstName&!secondName)设置secondName(项目)
else if(firstName&&secondName){
setFirstName(项目);
setSecondName(空)
}
}
返回(
    { names.map(i=>{ 返回( ) }) }
) } 常量项=({Item,…props})=>{ 常量{id,name,red}=item; const{handleItemClick}=props; 返回(
  • handleItemClick(项目)} > {name}
  • ) } ReactDOM.render(,document.getElementById('root'))

    但该代码不能正常工作,当连续单击两个相同的名称时,它们不会保持红色并再次变黑

    在我看来,问题似乎是事件处理程序过载,违反了单一责任原则

    处理程序应该负责处理click事件,而不负责其他任何事情。在这种情况下,单击元素时,您希望将
    id
    添加到所选/拾取名称的状态中,并切换具有匹配
    id
    的项目的
    red
    状态值。将超时效果考虑到(奇怪的是)一个
    useffect
    hook中,并将选取作为依赖项。这将使超时逻辑反转为清除/重置状态,而不是设置“红色”与否。您还可以/应该将确定匹配的任何逻辑移到相同的效果中(因为它已经具有依赖关系)

    这将允许您简化名称设置逻辑,以

    if (!firstName) {
      setFirstName(item);
    } else {
      setSecondName(item);
    }
    
    注意:我认为您需要另一个数据结构来保存/跟踪/存储用户进行的现有匹配

    工作原理:

  • 从干净状态开始,不选择任何名称
  • 选择名字时,
    firstName
    为空并更新,红色状态更新
  • 已设置超时(但尚未清除状态)
  • 选择第二个名称时,将定义
    firstName
    ,因此更新
    secondName
    ,更新红色状态
  • 如果匹配,将匹配添加到状态(保持红色)
  • 超时过期并重置状态(返回步骤1)
  • 下面是我如何尝试更简化一点状态,使用一个选定的
    id
    s数组,该数组仅在选定的
    id
    尚未选定且2个拾取尚未选定时更新

    const App = () => {
      const [names, setNames] = useState(items);
      const [picks, setPicks] = useState([]);
      const [matched, setMatched] = useState({});
    
      /**
       * On click event, add id to `picks` array, allow only two picks
       */
      const onClickHandler = id => () =>
        picks.length !== 2 &&
        !picks.includes(id) &&
        setPicks(picks => [...picks, id]);
    
      /**
       * Effect to toggle red state if id is included in current picks
       */
      useEffect(() => {
        setNames(names =>
          names.map(name => ({
            ...name,
            red: picks.includes(name.id)
          }))
        );
      }, [picks]);
    
      /**
       * Effect checks for name match, if a match is found it is added to the
       * `matched` array.
       */
      useEffect(() => {
        // matches example: { mark: 1, peter: 0, john: 0 }
        const matches = names.reduce((matches, { name, red }) => {
          if (!matches[name]) matches[name] = 0;
          red && matches[name]++;
          return matches;
        }, {});
    
        const match = Object.entries(matches).find(([_, count]) => count === 2);
    
        if (match) {
          const [matchedName] = match;
          setMatched(matched => ({ ...matched, [matchedName]: matchedName }));
        }
    
        const timer = setTimeout(() => {
          if (picks.length === 2) {
            setPicks([]);
            setNames(names => names.map(name => ({ ...name, red: false })));
          }
        }, 1000);
        return () => clearTimeout(timer);
      }, [names, picks]);
    
      return (
        <div className="App">
          <ul>
            {names.map(item => (
              <Item
                key={item.id}
                item={item}
                matches={matched}
                onClick={onClickHandler(item.id)}
              />
            ))}
          </ul>
        </div>
      );
    };
    
    const Item = ({ item, matches, ...props }) => {
      const { name, red } = item;
      return (
        <li
          className={classnames({
            red: red || matches[name], // for red text color
            matched: matches[name] // any other style to make matches stand out
          })}
          {...props}
        >
          {name}
        </li>
      );
    };
    
    const-App=()=>{
    const[names,setNames]=useState(项);
    const[picks,setPicks]=useState([]);
    const[matched,setMatched]=useState({});
    /**
    *单击事件时,将id添加到“picks”数组,只允许两次picks
    */
    const onClickHandler=id=>()=>
    长度!==2&&
    !picks.includes(id)&&
    setPicks(picks=>[…picks,id]);
    /**
    *如果当前拾取中包含id,则切换红色状态的效果
    */
    useffect(()=>{
    集合名称(名称=>
    names.map(name=>({
    名称
    红色:picks.includes(name.id)
    }))
    );
    },[picks]);
    /**
    *效果检查名称匹配,如果找到匹配项,则将其添加到
    *`matched`数组。
    */
    useffect(()=>{
    //匹配示例:{mark:1,peter:0,john:0}
    const matches=names.reduce((匹配,{name,red})=>{
    如果(!matches[name])匹配[name]=0;
    红色和匹配[名称]+;
    返回比赛;
    }, {});
    const match=Object.entries(matches.find)([[uu,count])=>count==2);
    如果(匹配){
    常量[matchedName]=匹配;
    setMatched(matched=>({…matched,[matchedName]:matchedName}));
    }
    常量计时器=设置超时(()=>{
    if(picks.length==2){
    集选([]);
    setNames(name=>names.map(name=>({…name,红色:false}));
    }
    }, 1
    
    const App = () => {
      const [names, setNames] = useState(items);
      const [picks, setPicks] = useState([]);
      const [matched, setMatched] = useState({});
    
      /**
       * On click event, add id to `picks` array, allow only two picks
       */
      const onClickHandler = id => () =>
        picks.length !== 2 &&
        !picks.includes(id) &&
        setPicks(picks => [...picks, id]);
    
      /**
       * Effect to toggle red state if id is included in current picks
       */
      useEffect(() => {
        setNames(names =>
          names.map(name => ({
            ...name,
            red: picks.includes(name.id)
          }))
        );
      }, [picks]);
    
      /**
       * Effect checks for name match, if a match is found it is added to the
       * `matched` array.
       */
      useEffect(() => {
        // matches example: { mark: 1, peter: 0, john: 0 }
        const matches = names.reduce((matches, { name, red }) => {
          if (!matches[name]) matches[name] = 0;
          red && matches[name]++;
          return matches;
        }, {});
    
        const match = Object.entries(matches).find(([_, count]) => count === 2);
    
        if (match) {
          const [matchedName] = match;
          setMatched(matched => ({ ...matched, [matchedName]: matchedName }));
        }
    
        const timer = setTimeout(() => {
          if (picks.length === 2) {
            setPicks([]);
            setNames(names => names.map(name => ({ ...name, red: false })));
          }
        }, 1000);
        return () => clearTimeout(timer);
      }, [names, picks]);
    
      return (
        <div className="App">
          <ul>
            {names.map(item => (
              <Item
                key={item.id}
                item={item}
                matches={matched}
                onClick={onClickHandler(item.id)}
              />
            ))}
          </ul>
        </div>
      );
    };
    
    const Item = ({ item, matches, ...props }) => {
      const { name, red } = item;
      return (
        <li
          className={classnames({
            red: red || matches[name], // for red text color
            matched: matches[name] // any other style to make matches stand out
          })}
          {...props}
        >
          {name}
        </li>
      );
    };