Javascript 反应警告:无法从其他组件的功能体内部更新组件

Javascript 反应警告:无法从其他组件的功能体内部更新组件,javascript,reactjs,redux,Javascript,Reactjs,Redux,我在React中使用Redux和类组件。在Redux存储中具有以下两种状态 { spinner: false, refresh: false } 在父组件中,我有一个分派函数来更改此状态 class App extends React.Component { reloadHandler = () => { console.log("[App] reloadComponent"); this.props.onShowSpinner(); this.props.

我在React中使用Redux和类组件。在Redux存储中具有以下两种状态

{ spinner: false, refresh: false }
在父组件中,我有一个分派函数来更改此状态

class App extends React.Component {
  reloadHandler = () => {
    console.log("[App] reloadComponent");

    this.props.onShowSpinner();
    this.props.onRefresh();
  };

  render() {
    return <Child reloadApp={this.reloadHandler} />;
  }
}
类应用程序扩展了React.Component{
重载处理程序=()=>{
log(“[App]重载组件”);
this.props.onShowSpinner();
this.props.onRefresh();
};
render(){
返回;
}
}
在子组件中,我尝试重新加载父组件,如下所示

class Child extends React.Component {
  static getDerivedStateFromProps(props, state) {
    if (somecondition) {
      // doing some redux store update
      props.reloadApp();
    }
  }

  render() {
    return <button />;
  }
}
类子级扩展React.Component{
静态getDerivedStateFromProps(props,状态){
如果(某些条件){
//做一些redux商店更新
props.reloadApp();
}
}
render(){
返回;
}
}
我得到的错误如下

import {setFilteredItems} from './myActions';
const myFilters = props => {
  const [nameFilter, setNameFilter] = useState('');
  const [cityFilter, setCityFilter] = useState('');

  useEffect(() => {
    const filterName = record => record.name.startsWith(nameFilter);
    const filterCity = record => record.city.startsWith(cityFilter);

    const selectedRecords = props.records.filter(rec => filterName(rec) && filterCity(rec));
    props.setFilteredItems(selectedRecords); //  <-- OK now; effect runs outside of render.
  }, [nameFilter, cityFilter]);

  return <div>
    <input type="text" value={nameFilter} onChange={e => setNameFilter(e.target.value)} />
    <input type="text" value={cityFilter} onChange={e => setCityFilter(e.target.value)} />
  </div>
};

const mapStateToProps = state => ({
  records: state.stuff.items,
  filteredItems: state.stuff.filteredItems
});
const mapDispatchToProps = { setFilteredItems };
export default connect(mapStateToProps, mapDispatchToProps)(myFilters);
警告:无法从组件的函数体内部更新组件 不同的组成部分


如何删除此警告?我在这里做错了什么?

您似乎拥有最新版本的
React@16.13.x
。你可以找到更多的细节。指定您不应从其他组件中设置另一组件的状态

从文档中:

支持在渲染期间调用setState,但仅限于同一组件。如果在渲染其他组件时调用setState,现在将看到一条警告:
警告:无法从其他组件的功能体内部更新组件。

此警告将帮助您查找由无意状态更改引起的应用程序错误。在极少数情况下,如果您有意更改另一个组件的状态作为呈现的结果,您可以将setState调用包装为useffect


说到实际问题

我认为在子组件主体中不需要
getDerivedStateFromProps
。如果要触发绑定事件。然后您可以通过子组件的
onClick
调用它,因为我可以看到它是一个

类子级扩展React.Component{
建造师(道具){
超级(道具);
this.updateState=this.updateState.bind(this);
}
updateState(){//调用此onClick以触发更新
如果(某些条件){
//做一些redux商店更新
this.props.reloadApp();
}
}
render(){
返回;
}
}

如果您想调用从子组件自动传递的作为道具的某些函数,那么最好的位置是componentDidMount生命周期方法(对于类组件)或useEffect挂钩(对于功能组件),因为此时组件已完全创建并已装载

对我来说,我是坐着一辆React Hook去我的redux商店。我必须在
useffect
中调度,以便与React渲染周期正确同步:

export const useOrderbookSubscription = marketId => {
  const { data, error, loading } = useSubscription(ORDERBOOK_SUBSCRIPTION, {
    variables: {
      marketId,
    },
  })

  const formattedData = useMemo(() => {
    // DISPATCHING HERE CAUSED THE WARNING
  }, [data])

  // DISPATCHING HERE CAUSED THE WARNING TOO

  // Note: Dispatching to the store has to be done in a useEffect so that React
  // can sync the update with the render cycle otherwise it causes the message:
  // `Warning: Cannot update a component from inside the function body of a different component.`
  useEffect(() => {
    orderbookStore.dispatch(setOrderbookData(formattedData))
  }, [formattedData])

  return { data: formattedData, error, loading }
}

我在编写带有几个文本框的过滤器组件时遇到了这个问题,这些文本框允许用户在另一个组件中限制列表中的项目。我在Redux状态下跟踪筛选的项目。该解决方案本质上是@Rajnikant的解决方案;使用一些示例代码

我收到警告是因为以下原因。注意渲染函数中的
props.setFilteredItems

import {setFilteredItems} from './myActions';
const myFilters = props => {
  const [nameFilter, setNameFilter] = useState('');
  const [cityFilter, setCityFilter] = useState('');

  const filterName = record => record.name.startsWith(nameFilter);
  const filterCity = record => record.city.startsWith(cityFilter);

  const selectedRecords = props.records.filter(rec => filterName(rec) && filterCity(rec));
  props.setFilteredItems(selectedRecords); //  <-- Danger! Updates Redux during a render!

  return <div>
    <input type="text" value={nameFilter} onChange={e => setNameFilter(e.target.value)} />
    <input type="text" value={cityFilter} onChange={e => setCityFilter(e.target.value)} />
  </div>
};

const mapStateToProps = state => ({
  records: state.stuff.items,
  filteredItems: state.stuff.filteredItems
});
const mapDispatchToProps = { setFilteredItems };
export default connect(mapStateToProps, mapDispatchToProps)(myFilters);

当我第一次添加
useffect
时,由于每次调用
useffect
都会导致状态更改,所以我把堆栈的顶部都炸了。我必须添加一组跳过效果,以便只有在过滤器字段本身发生更改时才会运行该效果。

注释了一些代码行,但这个问题是可以解决的:)出现此警告是因为您在其他类中同步调用
reloadApp
,请将调用推迟到
componentDidMount()

从“React”导入React;
导出默认类App扩展React.Component{
重载处理程序=()=>{
log(“[App]重载组件”);
//this.props.onShowSpinner();
//this.props.onRefresh();
};
render(){
返回;
}
}
子类扩展了React.Component{
静态getDerivedStateFromProps(props,状态){
//如果(某些条件){
//做一些redux商店更新
props.reloadApp();
// }
}
组件安装(道具){
如果(道具){
props.reloadApp();
}
}
render(){
这是一个孩子。;
}
}

如果您的代码在满足以下条件时调用父组件中的函数:

const ListOfUsersComponent = ({ handleNoUsersLoaded }) => {
    const { data, loading, error } = useQuery(QUERY);

    if (data && data.users.length === 0) {
        return handleNoUsersLoaded();
    }

    return (
        <div>
            <p>Users are loaded.</p>
        </div>
    );
};
相同的错误,但不同的场景 tl;dr
setTimeout
中包装状态更新将修复它

这种情况导致了IMO是有效用例的问题

const [someState, setSomeState] = useState(someValue);
const doUpdate = useRef((someNewValue) => {
  setSomeState(someNewValue);
}).current;
return (
  <SomeComponent onSomeUpdate={doUpdate} />
);

const[someState,setSomeState]=useState(someValue);
const doUpdate=useRef((someNewValue)=>{
setSomeState(someNewValue);
}).目前;
返回(
);
修复

const [someState, setSomeState] = useState(someValue);
const doUpdate = useRef((someNewValue) => {
  setTimeout(() => {
   setSomeState(someNewValue);
  }, 0);
}).current;
return (
  <SomeComponent onSomeUpdate={doUpdate} />
);

const[someState,setSomeState]=useState(someValue);
const doUpdate=useRef((someNewValue)=>{
设置超时(()=>{
setSomeState(someNewValue);
}, 0);
}).目前;
返回(
);

升级react和react native后,我也遇到了同样的问题,我只是通过将我的props.navigation.setOptions设置为useffect解决了这个问题。如果有人面临着与我相同的问题,我只想建议他将你的状态更改或任何内部使用的影响放到我的情况下,我错过了箭头函数
()=>{}

而不是
onDismiss={()=>{/*做点什么*/}

我把它当作
onDismiss={/*做点什么*/}


上帝可能在嘲笑我,认为人类就是人类

我建议看下面的视频。正如OP问题中的警告所表明的,由于另一个同级子级(子级1)回调到父级,父级(父级)试图提前更新一个子级(子级2)的属性时存在更改检测问题。对我来说,Child 2过早/错误地调用传入的父回调,因此抛出了警告

N
const ListOfUsersComponent = ({ handleNoUsersLoaded }) => {
    const { data, loading, error } = useQuery(QUERY);

    useEffect(() => {
        if (data && data.users.length === 0) {
            return handleNoUsersLoaded();
        }
    }, [data, handleNoUsersLoaded]);

    return (
        <div>
            <p>Users are loaded.</p>
        </div>
    );
};
const [someState, setSomeState] = useState(someValue);
const doUpdate = useRef((someNewValue) => {
  setSomeState(someNewValue);
}).current;
return (
  <SomeComponent onSomeUpdate={doUpdate} />
);

const [someState, setSomeState] = useState(someValue);
const doUpdate = useRef((someNewValue) => {
  setTimeout(() => {
   setSomeState(someNewValue);
  }, 0);
}).current;
return (
  <SomeComponent onSomeUpdate={doUpdate} />
);