【问题标题】:Can i set state in parent from child using useEffect hook in react我可以在反应中使用 useEffect 钩子从子级设置父级状态吗
【发布时间】:2020-05-07 12:44:16
【问题描述】:

我在子组件中有一组按钮,单击时设置相应的状态值 true 或 false。我在这个子组件中有一个 useEffect 钩子,它还依赖于所有这些状态值,所以如果单击一个按钮,这个钩子然后调用 setFilter ,它作为来自父级的道具传递下来......

const Filter = ({ setFilter }) => {

  const [cycling, setCycling] = useState(true);
  const [diy, setDiy] = useState(true);

  useEffect(() => {
    setFilter({
      cycling: cycling,
      diy: diy
    });
  }, [cycling, diy]);

  return (
    <Fragment>
      <Row>
        <Col>
          <Button block onClick={() => setCycling(!cycling)}>cycling</Button>
        </Col>
        <Col>
          <Button block onClick={() => setdIY(!DIY)}>DIY</Button>
        </Col>
      </Row>
    </Fragment>
  );
};

在父组件中,我显示了一个项目列表。我在父级中有两种效果,一种是初始加载项目,另一种是在更改过滤器时触发。为了简洁起见,我删除了大部分代码,但我认为我遇到的问题归结为这样一个事实,即在渲染我的 ItemDashboard 时,过滤器被调用了两次。我怎样才能阻止这种情况发生,或者我应该以另一种方式看待这个问题。

const ItemDashboard = () => {

  const [filter, setFilter] = useState(null);

  useEffect(() => {
    console.log('on mount');
  }, []);

  useEffect(() => {
    console.log('filter');
  }, [filter]);

  return (
    <Container>..
      <Filter setFilter={setFilter} />
    </Container>
  );
}

【问题讨论】:

  • 为什么不控制父组件中的所有状态变量?这里的主要问题是什么?它不是用 setFilter 方法更新父级的状态吗?
  • 是的,它确实会更新父状态,但对过滤器状态的影响会在加载时触发两次,在我的代码中,过滤器会影响从数据库中加载的项目两次。

标签: javascript reactjs react-hooks


【解决方案1】:

我猜,你正在寻找到lift state up 的方式来共同父母。

为了做到这一点,您可以将子组件的事件处理程序(作为道具传递)绑定到其共同父级中所需的回调。

以下现场演示演示了这一概念:

const { render } = ReactDOM,
      { useState } = React
      
const hobbies = ['cycling', 'DIY', 'hiking'] 

const ChildList = ({list}) => (
  <ul>
    {list.map((li,key) => <li {...{key}}>{li}</li>)}
  </ul>
)
      
const ChildFilter = ({onFilter, visibleLabels}) => (
  <div>
  {
    hobbies.map((hobby,key) => (
      <label {...{key}}>{hobby}
        <input 
          type="checkbox" 
          value={hobby}
          checked={visibleLabels.includes(hobby)}
          onChange={({target:{value,checked}}) => onFilter(value, checked)}
        />
      </label>))
  }
  </div>
)
      
const Parent = () => {
  const [visibleHobbies, setVisibleHobbies] = useState(hobbies),
        onChangeVisibility = (hobby,visible) => {
          !visible ?
          setVisibleHobbies(visibleHobbies.filter(h => h != hobby)) :
          setVisibleHobbies([...visibleHobbies, hobby])       
        }
   return (
      <div>
        <ChildList list={visibleHobbies} />
        <ChildFilter onFilter={onChangeVisibility} visibleLabels={visibleHobbies} />
      </div>
   )
}

render (
  <Parent />,
  document.getElementById('root')
)
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"&gt;&lt;/script&gt;&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.11.0/umd/react-dom.production.min.js"&gt;&lt;/script&gt;&lt;div id="root"&gt;&lt;/div&gt;

【讨论】:

    【解决方案2】:

    是的,您可以,useEffect 在依赖于状态的子组件中也是您通常如何实现受控和不受控的组件的方式:

    const NOOP = () => {};
    
    // Filter
    const Child = ({ onChange = NOOP }) => {
      const [counter, setCounter] = useState(0);
    
      useEffect(() => {
        onChange(counter);
      }, [counter, onChange]);
    
      const onClick = () => setCounter(c => c + 1);
    
      return (
        <div>
          <div>{counter}</div>
          <button onClick={onClick}>Increase</button>
        </div>
      );
    };
    
    // ItemDashboard
    const Parent = () => {
      const [value, setState] = useState(null);
    
      useEffect(() => {
        console.log(value);
      }, [value]);
    
      return <Child onChange={setState} />;
    };
    

    【讨论】:

    • 这基本上是我已经拥有的,但感谢您的回答。我觉得我问的问题不对,所以再问一个。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-05-23
    • 1970-01-01
    • 2020-05-11
    • 2021-03-08
    • 2020-10-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多