反应功能组件未从父组件更新 setState

Posted

技术标签:

【中文标题】反应功能组件未从父组件更新 setState【英文标题】:React functional component not updating on setState from parent component 【发布时间】:2022-01-16 22:29:13 【问题描述】:

我的 React 组件使用 apollo 通过 graphql 获取数据

class PopUpForm extends React.Component 

  constructor () 
    super()
    this.state = 
      shoptitle: "UpdateMe",
      popupbodyDesc: "UpdateMe"
    
  

render() 

    return (
        <>
        <Query query=STORE_META>
          ( data, loading, error, refetch ) => 
            
       
            if (loading) return <div>Loading…</div>;
            if (error) return <div>error.message</div>;
            if (!data) return (
              <p>Could not find metafields :(</p>
            );

            console.log(data);
             //loop over data
              var loopedmetafields = data.shop.metafields.edges
              console.log(loopedmetafields)
              loopedmetafields.forEach(element => 
                console.log(element.node.value)
                if (element.node.value === "ExtraShopDescription")
                  
                  this.setState(
                    shoptitle: element.node.value
                  );
                  console.log(this.state.shoptitle)
                
                if (element.node.value === "bodyDesc")
                   
                  this.setState(
                    popupbodyDesc: element.node.value
                  );
                  console.log(this.state.popupbodyDesc)
                
              );
            return (
              <>
                <AddTodo  mkey="ExtraShopDesc" namespace="ExtraShopDescription" desc=this.state.shoptitle onUpdate=refetch />
                <AddTodo  mkey="body" namespace="bodyDesc" desc=this.state.popupbodyDesc onUpdate=refetch />
              </>
            );
        
        </Query>
        </>
    )
    


export default PopUpForm

令人沮丧的是,功能组件在查询设置状态之前呈现。理想情况下,功能组件只会在此之后呈现,因为我认为它已被烘焙到 apollo 库中,但似乎我错了,它似乎执行同步而不是异步

如你所见,我将 props 传递给子组件,在子组件中我使用这些来显示某人可能修改的当前值

功能组件在这里

 function AddTodo(props) 

let input;
const [desc, setDesc] = useState(props.desc);
//console.log(desc)

useEffect( () => 
  console.log('props updated');
  console.log(props)
, [props.desc])

const [addTodo,  data, loading, error ] = useMutation(UPDATE_TEXT, 
    refetchQueries: [
        'STORE_META' // Query name
      ],
);

//console.log(data)
if (loading) return 'Submitting...';
if (error) return `Submission error! $error.message`;

return (
  <div>
    <form
      onSubmit=e => 
        console.log(input.value)
        setDesc(input.value)
        e.preventDefault();
        const newmetafields = 
            key: props.mkey,
            namespace: props.namespace,
            ownerId: "gid://shopify/Shop/55595073672",
            type: "single_line_text_field",
            value: input.value
        
        addTodo( variables:  metafields: newmetafields  );
        input.value = input.value
      
    >
        <p>This field denotes the title of your pop-up</p>
      <input className="titleInput" defaultValue=desc 
        ref=node => 
          input = node;
        
      />
      <button className="buttonClick" type="submit">Update</button>
    </form>

  </div>
);

现在我需要在 PopUpForm 上调用 setState 时更新此组件

另一个堆栈溢出答案here给了我一些线索

将初始状态作为道具传递给组件是一种反模式 因为 getInitialState(在我们的例子中是构造函数)方法是 仅在组件第一次渲染时调用。再也不会了。意义 也就是说,如果您重新渲染该组件,将不同的值作为 prop,组件不会做出相应的反应,因为组件 将保持第一次呈现时的状态。这是很 容易出错。

因此我随后实现了 useEffect,但是 useEffect 中的 console.log 仍然是“updateMe”,而不是从 graphql 调用返回的值。

所以我在哪里

我需要在 grapql 调用之后渲染功能组件 而且我已经操纵了数据,这似乎也是设计模式方面的最佳方法

我需要 setState 来传递/渲染具有新值的功能组件

顺便说一句,如果我这样做

            <AddTodo  mkey="ExtraShopDesc" namespace="ExtraShopDescription" desc=data.shop.metafields.edges[0].node.value onUpdate=refetch /> 

它会起作用,但我不能总是期望值是 0 或 1,因为元字段可能已经定义了

【问题讨论】:

【参考方案1】:

我认为有比使用setState 更简单的方法来解决这个问题。例如,您可以像这样使用find

              const shopTitleElement = loopedmetafields.find(element => 
                return element.node.value === "ExtraShopDescription"
              )
              const shopBodyElement = loopedmetafields.find(element => 
                return element.node.value === "bodyDesc"
              );
              return (
              <>
                <AddTodo  mkey="ExtraShopDesc" namespace="ExtraShopDescription" desc=shopTitleElement.node.value onUpdate=refetch />
                <AddTodo  mkey="body" namespace="bodyDesc" desc=shopBodyElement.node.value onUpdate=refetch />
              </>
            );

【讨论】:

以上是关于反应功能组件未从父组件更新 setState的主要内容,如果未能解决你的问题,请参考以下文章

在功能组件中使用回调对 setState 进行反应

Vue子组件未从父组件接收道具

使用反应挂钩将基于类的组件转换为功能组件[重复]

reactjs中未从父级调用子组件函数

尝试在反应组件的返回中使用 setstate 更新状态并获得“最大更新深度超出错误”?

ReactJS setState 更改不会从父组件传播到子组件[重复]