【问题标题】:Re-render Component after Redux state change not working?Redux 状态更改后重新渲染组件不起作用?
【发布时间】:2021-07-08 17:04:10
【问题描述】:

我正在使用 react-flow-maker 库构建一个小项目。该库使您能够使用对象创建自己的流程图。一个对象可以有它自己的字段,比如文本框、开关和下拉菜单。

这个库是如何工作的?

该库具有以下反应组件。

<FlowMaker
  logic={{
    introComponents: [],
    components: [],
  }}
  onChange={data => localStorage.setItem('flowMakerExample', JSON.stringify(data))}
  flow={JSON.parse(localStorage.getItem('flowMakerExample'))}
/>

该组件中使用的props具有以下功能:

  • logic -> 逻辑描述了模块和它们的输入。例如,它需要一个具有以下属性的对象。

    let logic = {
         introComponents: [
             'hello-world'
         ]
    
         components: [
            {
              name: 'hello-world'
              title: 'Hello World'
              inputs: [
                  {
                     name: 'options',
                     title: 'Options',
                     type: 'dropdown',
                     default: 'c',
                     options: [
                       {title: 'A', value: 'a'},
                       {title: 'B', value: 'b'},
                       {title: 'C', value: 'c'},
                       {title: 'D', value: 'd'},
                       {title: 'E', value: 'e'},
                     ]
                  }
              ],
              next: 'hello-world'
            }
         ]
    }
    
    • onChange -> 这将返回用户更改某些内容时的流数据
    • flow -> 在这里,您可以添加一个流程来显示绘图何时挂载,如果您从屏幕上移除组件或绘图需要持久化,则非常方便。

    我的目标:

    1. 创建一个带有下拉列表的块,通过 API 获取项目列表并将它们作为标题和值放入下拉列表中
    2. 如果用户更改图表中的某些内容,请执行新的提取并更新下拉列表的选项。

    我已经实现了一个返回以下 JSON 列表的 GET 请求:

    [
        {"name":"name_0","sid":"0"}, 
        {"name":"name_1","sid":"1"},
        {"name":"name_2","sid":"2"}, 
        {"name":"name_3","sid":"3"}
    ]
    

Logic.js 此文件包含 FlowMaker 组件中使用的逻辑。在这里,我将 applications 映射到 dorpdown 中使用的选项的正确格式。

    const Logic = async (applications, ..., ...) => {
      return {
        introComponents: [
          'hello-world'
        ],
    
        components: [
          {
            name: 'hello-world',
            title: 'hello world',
            tooltip: 'This is hello',
            inputs: [
              ...
              {
                name: 'applicationName',
                title: 'Application name',
                type: 'dropdown',
                options: [
                  ...applications.map(app => (
                    {title: app.name, value: app.name + ' - ' + app.sid})
                  )
                ]
              },
              ...
            ],
            next: 'hello-world'
          },
          ...
        ]
      }
    }
    export default Logic;
    

drawerReducer.js 我的 reducer,我在其中初始化此抽屉的新状态。

const initialState = {
    logic: null,
    data: null,
    applications: [],
    ...
}

const drawerReducer = (state = initialState, action) => {
    switch(action.type) {
        case LOGIC:
            return {
                ...state, 
                logic: action.payload
            }
        case DATA:
            return {
                ...state,
                data: action.payload
            }
        case APPLICATIONS: 
            return {
                ...state,
                applications: action.payload
            }
        ...
        default:
            return state;
    }
}

export default drawerReducer;

drawerAction.js 包含我在其中获取新应用程序、设置新数据和逻辑的操作。

...
import Logic from '../utils/Logic'
import { LOGIC, APPLICATIONS, ..., ..., DATA } from './types'

export const setLogic = (applications, ..., ...) => dispatch => {
    Logic(applications, ..., ...)
    .then(newLogic => dispatch({
        type: LOGIC,
        payload: newLogic
    }))
}

export const setData = (newData) => dispatch => {
    dispatch({
        type: DATA,
        payload: newData
    })
}

export const setApplications = () => dispatch => {
    ApplicationList()
    .then(newApplications => dispatch({
        type: APPLICATIONS,
        payload: newApplications
    }))
} 

...

drawing.js 在这里我放置了 FlowMaker 组件并将所有内容放在一起。你可以看到我正在使用 useEffect hook 来更新应用程序,然后在 data prop 更改时更新逻辑。

import React, {useEffect} from 'react'
import FlowMaker from 'flowmaker'
import '../styles/flowmaker.css'
import Loader from '../utils/Loader'
import {setApplications, setData, setLogic } from '../actions/drawerAction'
import { connect } from 'react-redux'

const Drawer = ({logic, data, applications, doSetLogic, doSetData, doSetApplications}) => {

    useEffect(() => {
        doSetApplications() //dispatch new applications
        doSetLogic(applications) //dispatch to set the new logic with the newly fetched applications
        return () => {
            //cleanup
        }
    }, [data])

    return (
        <div className='drawer-canvas'>
                { logic ? 
                <>
                    <ButtonGroup />
                    <FlowMaker
                    logic={logic} //intial state of the diagramoptions
                    onChange={newData => doSetData(newData)}
                    flow={data}
                    />
                </>
                : <Loader />
                }
            </div>
    )
}

const mapStateToProps = state => ({
    logic: state.drawer.logic,
    data: state.drawer.data,
    applications: state.drawer.applications,
    ...
})

const mapDispatchToProps = {
    doSetLogic: setLogic,
    doSetData: setData,
    doSetApplications: setApplications,
    ...
}

export default connect(mapStateToProps, mapDispatchToProps)(Drawer) 

我的问题 我的问题是当 useEffect 数据依赖被击中时。该图没有将我图中的新应用程序选项重新呈现为新选项,而 Redux 中的逻辑状态确实发生了变化。

这是我在执行数据更改操作之前的逻辑状态。您可以看到选项是一个空列表。

现在我在图表中添加了一个新块。这意味着 data 动作将被触发并且 newData 将被设置为数据,接下来由于依赖 [data] 触发了 useEffect 并且使用新逻辑设置了逻辑,这意味着 applicationName 下拉列表必须填充新选项和没错。

现在完成了一个新的 redux 逻辑操作,我希望选项存在,但它们不存在,这很奇怪,因为在第二张图片中你可以看到逻辑确实更新了。

总结;我的问题是如何使用新设置的 Redux 状态重新渲染这个组件?我认为当您更改 redux 状态时,会像 setState 一样自动触发重新渲染。对这个问题有什么想法吗?

我知道这是很多文本/代码/图片,对此我很抱歉,我只是没有更好的想法如何做到这一点。

【问题讨论】:

    标签: javascript reactjs redux use-effect


    【解决方案1】:

    从本周开始,我使用的 package 有了新的更新。此更新可以使用 getInputs 函数在特定数据更改时重新渲染组件项。在example main.js 文件中有一个关于此的示例逻辑。

    {
          name: 'frontend',
          tooltip: 'Information about the proxy/load balancing server',
          title: 'Frontend',
          getInputs(info) {
            const isHttpsInputs = info.inputs.https ? [
              {
                name: 'sslCert',
                title: 'Add ssl cert',
                tooltip: 'Add a ssl certificate',
                type: 'switch',
                default: true,
              }
            ] : [];
            return [
              {
                name: 'server',
                title: 'Server',
                type: 'text',
                tooltip: 'The address of the proxy/load balancing server',
                validation: domainCheck,
              }, {
                name: 'https',
                title: 'The server traffic is https',
                type: 'switch',
                default: true,
              },
              ...isHttpsInputs,
              {
                name: 'port',
                title: 'Web server port',
                type: 'number',
                default: 443,
                validation: portCheck,
              }
            ]
          },
          next: 'backend'
        },
    
    

    getInputs 将检查 info.inputs.https 是否被检查,如果为 true,则将基于此添加(或更新)一个额外的字段。

    【讨论】:

      【解决方案2】:

      在您的 useEffect 中,您似乎正在调度想要使用更新状态的操作,但是更新的状态不是立即的,并且受闭包的影响。

      您需要将该逻辑拆分为另一个 useEffect

      useEffect(() => {
          doSetApplications() //dispatch new applications
      }, [data]);
      
      useEffect(() => {
        if(applications) {
          doSetLogic(applications) //dispatch to set the new logic with the newly fetched applications
        }
      }, [applications]);
      

      【讨论】:

      • 感谢您的建议。但是在将调度拆分为多个 useEffect 挂钩时,我仍然得到相同的结果。我确实尝试了一些不同的东西,我只是将drawerAction.js中的逻辑操作扩展为: ApplicationList().then(newApplications => { Logic(newApplications).then(newLogic => dispatch({type: LOGIC, payload: newLogic} )) } 我首先获取应用程序,然后设置我的逻辑。现在在第一次渲染时,应用程序字段已填充!但是当数据更改并调用 useEffect 挂钩时。组件不会重新渲染。
      • 我当前在drawerAction.js中的setLogic方法: export const setLogic = () => dispatch => { ApplicationList().then(newApplications => { Logic(newApplications).then(newLogic => dispatch ({type: LOGIC, payload: newLogic})) }) }
      猜你喜欢
      • 1970-01-01
      • 2020-07-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-03-26
      • 2018-12-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多